基于Vue实现跨表格(单选、多选表格项,单表格限制)相互拖拽

时间:2022-11-26 09:19:31

基于Vue实现跨表格(单选、多选表格项,单表格限制)相互拖拽

前言

今天,我们将使用Vue.js来实现一个跨表格相互拖拽。在开发这个业务之前呢,也调研了网上很多解决方案,但个人感觉不太符合现在做的这个需求。所以,压根就自己再开发一套,方便以后维护。

什么需求呢?就是多个表格之间可以实现相互拖拽,即A表格中的表格项可以拖拽到B表格,B表格的表格项可以拖拽到C表格,并且它们之间可以单选、多选表格项相互拖拽。然后,D表格加以限制,每次只能够拖入一项,需输入密码,密码正确后,被拖入的一项替换D表格中的表格项,被替换的D表格项放入A表格,只能被替换,不能被删除。

文字太枯燥,我们放一张动图来看下效果。

上面提到的A、B、C、D表格与下图游客、操作员、电工、管理员一一对应。

基于Vue实现跨表格(单选、多选表格项,单表格限制)相互拖拽

此图非静止~

实战

既然,我们知道了要实现怎样的效果,那么我们就开工吧!

第一步

需要确定我们这个需求需要安装哪些依赖。

  • vue@2.6.11
  • element-ui@2.15.5
  • sortablejs@1.14.0

初始化项目之后,安装以上依赖。你可以在package.json文件中看到:

  1. "dependencies": { 
  2.   "element-ui""^2.15.5"
  3.   "sortablejs""^1.14.0"
  4.   "vue""^2.6.11" 
  5. }, 

第二步

引入ElementUI,具体怎么引入,可以查看ElementUI官网,这里不过多阐述了。然后,我们在目录src\components(假设你有这个文件夹)文件夹下创建一个文件夹,名字姑且叫DragTables。在文件夹中,我们再创建一个utils文件夹与index.vue文件。在utils文件夹中我们再创建两个文件:data.js与index.js。

即文件目录结构为:

  1. - components 
  2. -- DragTables 
  3. --- utils 
  4. ---- data.js 
  5. ---- index.js 
  6. --- index.vue 

第三步

utils\data.js文件是存放数据的文件,而utils\index.js则是工具函数文件。

现在我们先来定义数据。

  1. // data.js 
  2. export default { 
  3.     // 游客 
  4.     guestData: [ 
  5.     { 
  6.         userId: "1"
  7.         name"a1"
  8.         account: "w"
  9.         jobTit: '1'
  10.         auth: '1' 
  11.     }, 
  12.     { 
  13.         userId: "211"
  14.         name"a0"
  15.         account: "w"
  16.         jobTit: '1'
  17.         auth: '1' 
  18.     } 
  19.     ], 
  20.     // 管理员 
  21.     managerData: [ 
  22.     { 
  23.         userId: "121"
  24.         name"a2"
  25.         account: "w"
  26.         jobTit: '1'
  27.         auth: '1' 
  28.     } 
  29.     ], 
  30.     // 电工 
  31.     electricianData: [ 
  32.     { 
  33.         userId: "12121"
  34.         name"a3"
  35.         account: "w"
  36.         jobTit: '1'
  37.         auth: '1' 
  38.     } 
  39.     ], 
  40.     // 操作员 
  41.     operatorData: [ 
  42.     { 
  43.         userId: "133e"
  44.         name"a4"
  45.         account: "w"
  46.         jobTit: '1'
  47.         auth: '1' 
  48.     } 
  49.     ] 

userId必须是唯一的。

然后,我们接着定义工具函数,这里我们需要一个深拷贝方法,我们把它定义在utils\index.js文件中。

  1. // utils\index.js 
  2.  
  3. /** 
  4.  * Deep copy 
  5.  * @param {Object} target 
  6.  */ 
  7. export function deepClone(target) { 
  8.     // 定义一个变量 
  9.     let result; 
  10.     // 如果当前需要深拷贝的是一个对象的话 
  11.     if (typeof target === 'object') { 
  12.         // 如果是一个数组的话 
  13.         if (Array.isArray(target)) { 
  14.             result = []; // 将result赋值为一个数组,并且执行遍历 
  15.             for (let i in target) { 
  16.                 // 递归克隆数组中的每一项 
  17.                 result.push(deepClone(target[i])); 
  18.             } 
  19.             // 判断如果当前的值是null的话;直接赋值为null 
  20.         } else if (target === null) { 
  21.             result = null
  22.             // 判断如果当前的值是一个RegExp对象的话,直接赋值 
  23.         } else if (target.constructor === RegExp) { 
  24.             result = target; 
  25.         } else { 
  26.             // 否则是普通对象,直接for in循环,递归赋值对象的所有值 
  27.             result = {}; 
  28.             for (let i in target) { 
  29.                 result[i] = deepClone(target[i]); 
  30.             } 
  31.         } 
  32.         // 如果不是对象的话,就是基本数据类型,那么直接赋值 
  33.     } else { 
  34.         result = target; 
  35.     } 
  36.     // 返回最终结果 
  37.     return result; 

第四步

做完了准备工作,我们就可以进入DragTables\index.vue进行最重要的实战环节。我们先把代码分区域列出来。

1. UI页面代码

  1. <template> 
  2.     <div 
  3.       class="main-box" 
  4.       v-loading="loadLoading" 
  5.       element-loading-text="数据加载中" 
  6.       element-loading-spinner="el-icon-loading" 
  7.     > 
  8.       <div class="main-l"
  9. <!-- 游客 --> 
  10.         <div class="top-name"
  11.           <div class="top-box"
  12.             <div class="top-count"
  13.               人数: 
  14.               {{ 
  15.                 initStatus.newGuestList ? guestData.length : newGuestList.length 
  16.               }} 
  17.             </div> 
  18.           </div> 
  19.           <p>游客</p> 
  20.         </div> 
  21.         <div class="utable-box"
  22.           <el-table 
  23.             ref="guestData" 
  24.             :data="guestData" 
  25.             :row-key="getUserId" 
  26.             @row-click="guestDataSelect" 
  27.             style="width: 100%; border: 1px solid #002368; margin-bottom: 20px" 
  28.             @selection-change="selectionGuestChange" 
  29.           > 
  30.             <el-table-column type="selection" width="55"> </el-table-column
  31.             <el-table-column 
  32.               prop="name" 
  33.               label="姓名" 
  34.               align="center" 
  35.               show-overflow-tooltip 
  36.             ></el-table-column
  37.             <el-table-column 
  38.               prop="account" 
  39.               label="账号" 
  40.               align="center" 
  41.               show-overflow-tooltip 
  42.             > 
  43.             </el-table-column
  44.             <el-table-column 
  45.               prop="jobTit" 
  46.               label="职务" 
  47.               align="center" 
  48.               show-overflow-tooltip 
  49.               style="position: relative" 
  50.             > 
  51.               <template slot-scope="scope"
  52.                 <span>{{ scope.row.jobTit }}</span> 
  53.               </template> 
  54.             </el-table-column
  55.           </el-table
  56.         </div> 
  57.       </div> 
  58.       <div class="main-r"
  59. <!-- 管理员 --> 
  60.         <div class="top-name"
  61.           <p>管理员</p> 
  62.         </div> 
  63.         <el-table 
  64.           ref="managerData" 
  65.           :data="managerData" 
  66.           :row-key="getUserId" 
  67.           style="width: 100%; border: 1px solid #002368; margin-bottom: 10px" 
  68.         > 
  69.           <el-table-column 
  70.             prop="name" 
  71.             label="姓名" 
  72.             align="center" 
  73.             show-overflow-tooltip 
  74.           > 
  75.           </el-table-column
  76.           <el-table-column 
  77.             prop="account" 
  78.             label="账号" 
  79.             align="center" 
  80.             show-overflow-tooltip 
  81.           > 
  82.           </el-table-column
  83.           <el-table-column 
  84.             prop="jobTit" 
  85.             label="职务" 
  86.             align="center" 
  87.             style="position: relative" 
  88.             show-overflow-tooltip 
  89.           > 
  90.             <template slot-scope="scope"
  91.               <span>{{ scope.row.jobTit }}</span> 
  92.             </template> 
  93.           </el-table-column
  94.         </el-table
  95. <!-- 操作员 --> 
  96.         <div class="top-name"
  97.           <div class="top-box"
  98.             <div class="top-count"
  99.               人数: 
  100.               {{ 
  101.                 initStatus.newOperatorList 
  102.                   ? operatorData.length 
  103.                   : newOperatorList.length 
  104.               }} 
  105.             </div> 
  106.           </div> 
  107.           <p>操作员</p> 
  108.         </div> 
  109.         <div class="table-b"
  110.           <div class="table-box"
  111.             <el-table 
  112.               ref="operatorData" 
  113.               class="table-l" 
  114.               :data="operatorData" 
  115.               :row-key="getUserId" 
  116.               style="margin-bottom: 20px" 
  117.               @row-click="operatorDataSelect" 
  118.               @selection-change="selectionOperatorChange" 
  119.             > 
  120.               <el-table-column type="selection" width="55"> </el-table-column
  121.               <el-table-column 
  122.                 prop="name" 
  123.                 label="姓名" 
  124.                 align="center" 
  125.                 show-overflow-tooltip 
  126.               > 
  127.               </el-table-column
  128.               <el-table-column 
  129.                 prop="account" 
  130.                 label="账号" 
  131.                 align="center" 
  132.                 show-overflow-tooltip 
  133.               > 
  134.               </el-table-column
  135.               <el-table-column 
  136.                 prop="jobTit" 
  137.                 label="职务" 
  138.                 align="center" 
  139.                 show-overflow-tooltip 
  140.               ></el-table-column
  141.               <el-table-column align="center" label="操作"
  142.                 <template> 
  143.                    <el-button size="small" @click.stop="">编辑</el-button> 
  144.                 </template> 
  145.               </el-table-column
  146.             </el-table
  147.           </div> 
  148.         </div> 
  149. <!-- 电工 --> 
  150.         <div class="top-name"
  151.           <div class="top-box"
  152.             <div class="top-count"
  153.               人数: 
  154.               {{ 
  155.                 initStatus.newElectricianList 
  156.                   ? electricianData.length 
  157.                   : newElectricianList.length 
  158.               }} 
  159.             </div> 
  160.           </div> 
  161.           <p>电工</p> 
  162.         </div> 
  163.         <div class="table-b"
  164.           <div class="table-box"
  165.             <el-table 
  166.               ref="electricianData" 
  167.               :data="electricianData" 
  168.               :row-key="getUserId" 
  169.               @row-click="electricianDataSelect" 
  170.               class="table-l" 
  171.               @selection-change="selectionElectricianChange" 
  172.             > 
  173.               <el-table-column type="selection" width="55"> </el-table-column
  174.               <el-table-column 
  175.                 prop="name" 
  176.                 label="姓名" 
  177.                 align="center" 
  178.                 show-overflow-tooltip 
  179.               > 
  180.               </el-table-column
  181.               <el-table-column 
  182.                 prop="account" 
  183.                 label="账号" 
  184.                 align="center" 
  185.                 show-overflow-tooltip 
  186.               > 
  187.               </el-table-column
  188.               <el-table-column 
  189.                 prop="jobTit" 
  190.                 label="职务" 
  191.                 align="center" 
  192.                 show-overflow-tooltip 
  193.               ></el-table-column
  194.               <el-table-column align="center" label="操作"
  195.                 <template> 
  196.                     <el-button size="small" @click.stop="">编辑</el-button> 
  197.                 </template> 
  198.               </el-table-column
  199.             </el-table
  200.           </div> 
  201.         </div> 
  202.       </div> 
  203. <!-- 密码弹窗 --> 
  204.       <el-dialog :visible.sync="passwordView"  width="30%"
  205.         <el-form ref="form"
  206.           <el-form-item> 
  207.             <el-input 
  208.               v-model="password" 
  209.               placeholder="请输入密码" 
  210.               type="password" 
  211.               show-password 
  212.             ></el-input> 
  213.           </el-form-item> 
  214.         </el-form> 
  215.         <span slot="footer" class="dialog-footer"
  216.           <el-button @click="passwordView = false">取 消</el-button> 
  217.           <el-button type="primary" @click="okChangeManager" 
  218.             >确 定</el-button 
  219.           > 
  220.         </span> 
  221.       </el-dialog> 
  222.     </div> 
  223. </template> 

分为五个部分:游客表格、管理员表格、操作员表格、电工表格、密码弹窗。每个表格的左上角动态显示表格内的人数。另外,就上面的那个动图来看,如果有一个表格与其他的表格样式布局不统一怎么办?就比如这里的电工表格、操作员表格就与游客表格、管理员表格布局样式不一样,多出来一个操作项。我是这样处理的,我把它们找出相同的部分,即都有姓名、账号、职务这三个项。电工表格、操作员表格只是多出来一个操作项。那就可以把它分成两个表格,操作项单独一个表格。只要监听电工表格或者操作员表格它们对应的数据长度就可以实现同步。为什么要分成两个表格呢?是因为,如果你从游客这个表格拖入到操作员这个表格,因为在游客表格没有操作这个选项,所以当你拖入到操作员表格时,就不会有操作这个选项(这是因为使用的拖拽的插件只是复制对应Node节点)。那肯定不行啊!

2. 逻辑代码

  1. <script> 
  2. import Sortable from "sortablejs"
  3. import { deepClone } from "./utils/index"
  4. import tableData from "./utils/data"
  5.  
  6. export default { 
  7.   name"DragTables"
  8.   data: () => ({ 
  9.     passwordView: false
  10.     loadLoading: false
  11.     password""
  12.     guestData: [], 
  13.     managerData: [], 
  14.     electricianData: [], 
  15.     operatorData: [], 
  16.     initStatus: { 
  17.       newGuestList: true
  18.       newManagerList: true
  19.       newOperatorList: true
  20.       newElectricianList: true
  21.     }, 
  22.     fromItem: ""
  23.     newGuestList: [], 
  24.     newManagerList: [], 
  25.     newOperatorList: [], 
  26.     newElectricianList: [], 
  27.     selectGuestList: [], 
  28.     selectOperatorList: [], 
  29.     selectElectricianList: [], 
  30.     managerOldIndex: 0, 
  31.   }), 
  32.   watch: { 
  33.     passwordView: "watchPasswordView"
  34.   }, 
  35.   created() { 
  36.     // 定义静态数据 
  37.     this.obj = { 
  38.       newGuestList: ["guestData", 0], 
  39.       newManagerList: ["managerData", 3], 
  40.       newOperatorList: ["operatorData", 1], 
  41.       newElectricianList: ["electricianData", 2], 
  42.     }; 
  43.     this.guestData = tableData.guestData; 
  44.     this.managerData = tableData.managerData; 
  45.     this.electricianData = tableData.electricianData; 
  46.     this.operatorData = tableData.operatorData; 
  47.   }, 
  48.   mounted() { 
  49.     this.sortGuest(); 
  50.     this.sortOperator(); 
  51.     this.sortElectrician(); 
  52.     this.sortManager(); 
  53.   }, 
  54.   methods: { 
  55.     // 密码框置空 
  56.     watchPasswordView(val) { 
  57.       if (!val) { 
  58.         this.password = ""
  59.       } 
  60.     }, 
  61.     // 选择游客 
  62.     guestDataSelect(row) { 
  63.       row.flag = !row.flag; 
  64.       this.$refs.guestData.toggleRowSelection(row, row.flag); 
  65.     }, 
  66.     // 选择操作员 
  67.     operatorDataSelect(row) { 
  68.       row.flag = !row.flag; 
  69.       this.$refs.operatorData.toggleRowSelection(row, row.flag); 
  70.     }, 
  71.     // 选择电工 
  72.     electricianDataSelect(row) { 
  73.       row.flag = !row.flag; 
  74.       this.$refs.electricianData.toggleRowSelection(row, row.flag); 
  75.     }, 
  76.     // 确定拖拽到管理员 
  77.     okChangeManager() { 
  78.       if (this.password.trim().length > 0) { 
  79.         const item = this[this.fromItem][this.managerOldIndex]; 
  80.         if (item) { 
  81.           this.newManagerList = this.initStatus.newManagerList 
  82.             ? deepClone(this.managerData) 
  83.             : deepClone(this.newManagerList); 
  84.           this.newGuestList = this.initStatus.newGuestList 
  85.             ? deepClone(this.guestData) 
  86.             : deepClone(this.newGuestList); 
  87.           this.initStatus.newGuestList = false
  88.           this.initStatus.newManagerList = false
  89.           this.newManagerList = [item]; 
  90.           this[this.fromItem].splice(this.managerOldIndex, 1); 
  91.           this.initStatus[this.fromItem] = false
  92.  
  93.           if (this.managerData[0]) { 
  94.             const obj = deepClone(this.managerData[0]); 
  95.             this.newGuestList.push(obj); 
  96.             this.guestData.push(obj); 
  97.           } 
  98.           this.managerData = [item]; 
  99.           switch (this.fromItem) { 
  100.             case "newGuestList"
  101.               this.guestData = deepClone(this.newGuestList); 
  102.               break; 
  103.             case "newOperatorList"
  104.               this.operatorData = deepClone(this.newOperatorList); 
  105.               break; 
  106.             case "newElectricianList"
  107.               this.electricianData = deepClone(this.newElectricianList); 
  108.               break; 
  109.             default
  110.               break; 
  111.           } 
  112.           this.$message({ 
  113.             message: "拖拽成功!"
  114.             type: "success"
  115.           }); 
  116.           this.password = ""
  117.           this.passwordView = false
  118.         } else { 
  119.           this.$message({ 
  120.             message: "拖拽失败"
  121.             type: "warning"
  122.           }); 
  123.           this.password = ""
  124.           this.passwordView = false
  125.         } 
  126.       } else { 
  127.         this.$message({ 
  128.           message: "请输入密码"
  129.           type: "warning"
  130.         }); 
  131.       } 
  132.     }, 
  133.     // 获取userId 
  134.     getUserId(row) { 
  135.       return row.userId; 
  136.     }, 
  137.     // 封装添加数据 
  138.     useAddNewData(evt, newData, oldData) { 
  139.       const item = this[this.fromItem][evt.oldIndex]; // 添加项 
  140.       const loading = this.$loading({ 
  141.         lock: true
  142.         text: "加载中"
  143.         spinner: "el-icon-loading"
  144.         background: "rgba(0, 0, 0, 0.7)"
  145.       }); 
  146.  
  147.       setTimeout(() => { 
  148.         loading.close(); 
  149.         this.$message({ 
  150.           message: "拖拽成功!"
  151.           type: "success"
  152.         }); 
  153.       }, 1000); 
  154.       this[newData] = this.initStatus[newData] 
  155.         ? deepClone(oldData) 
  156.         : deepClone(this[newData]); 
  157.       this.initStatus[newData] = false
  158.       oldData.push(item); 
  159.       this[newData].push(item); 
  160.       this[this.fromItem].splice(evt.oldIndex, 1); 
  161.       this.$refs[this.obj[newData][0]].$el 
  162.         .querySelectorAll(".el-table__body-wrapper > table > tbody")[0] 
  163.         .removeChild(evt.item); 
  164.     }, 
  165.     // 封装添加(多)数据 
  166.     useAddsNewData(evt, newData, oldData) { 
  167.       const arr = []; 
  168.       for ( 
  169.         let index = 0; 
  170.         index < this[`select${this.fromItem.split("new")[1]}`].length; 
  171.         index++ 
  172.       ) { 
  173.         const element = this[`select${this.fromItem.split("new")[1]}`][index]; 
  174.         arr.push(element.userId); 
  175.       } 
  176.       const loading = this.$loading({ 
  177.         lock: true
  178.         text: "加载中"
  179.         spinner: "el-icon-loading"
  180.         background: "rgba(0, 0, 0, 0.7)"
  181.       }); 
  182.  
  183.       setTimeout(() => { 
  184.         loading.close(); 
  185.         this.$message({ 
  186.           message: "批量拖拽成功!"
  187.           type: "success"
  188.         }); 
  189.       }, 1000); 
  190.       this[newData] = this.initStatus[newData] 
  191.         ? deepClone(oldData) 
  192.         : deepClone(this[newData]); 
  193.       this.initStatus[newData] = false
  194.       this[newData].push(...this[`select${this.fromItem.split("new")[1]}`]); 
  195.       this[this.obj[newData][0]].push( 
  196.         ...this[`select${this.fromItem.split("new")[1]}`] 
  197.       ); 
  198.       this.useDel( 
  199.         this[`select${this.fromItem.split("new")[1]}`], 
  200.         this[this.obj[this.fromItem][0]] 
  201.       ); 
  202.       this.$refs[this.obj[newData][0]].$el 
  203.         .querySelectorAll(".el-table__body-wrapper > table > tbody")[0] 
  204.         .removeChild(evt.item); 
  205.     }, 
  206.     // 封装初始化数据 
  207.     useInitData(fromItem, oldData) { 
  208.       this.fromItem = fromItem; 
  209.       this[fromItem] = this.initStatus[fromItem] 
  210.         ? deepClone(oldData) 
  211.         : deepClone(this[fromItem]); 
  212.       this.initStatus[fromItem] = false
  213.     }, 
  214.     // 批量删除(数组) 
  215.     useDel(data, currentData) { 
  216.       for (let i = 0; i < data.length; i++) { 
  217.         const element = data[i]; 
  218.         for (let j = 0; j < currentData.length; j++) { 
  219.           const item = currentData[j]; 
  220.           if (item === element) { 
  221.             currentData.splice(j, 1); 
  222.           } 
  223.         } 
  224.       } 
  225.     }, 
  226.     // 还原初始状态 
  227.     useReduction(i) { 
  228.       const arr = [ 
  229.         { 
  230.           data: "guestData"
  231.           sletData: "selectGuestList"
  232.         }, 
  233.         { 
  234.           data: "operatorData"
  235.           sletData: "selectOperatorList"
  236.         }, 
  237.         { 
  238.           data: "electricianData"
  239.           sletData: "selectElectricianList"
  240.         }, 
  241.       ]; 
  242.       this.$refs[arr[i].data].clearSelection(); 
  243.       this[arr[i].sletData] = []; 
  244.     }, 
  245.     // 监听游客表格选择 
  246.     selectionGuestChange(val) { 
  247.       this.selectGuestList = val; 
  248.     }, 
  249.     // 监听操作员表格选择 
  250.     selectionOperatorChange(val) { 
  251.       this.selectOperatorList = val; 
  252.     }, 
  253.     // 监听电工表格选择 
  254.     selectionElectricianChange(val) { 
  255.       console.log(val); 
  256.       this.selectElectricianList = val; 
  257.     }, 
  258.     // 拖拽游客 
  259.     sortGuest() { 
  260.       const el = this.$refs.guestData.$el.querySelectorAll( 
  261.         ".el-table__body-wrapper > table> tbody" 
  262.       )[0]; 
  263.       Sortable.create(el, { 
  264.         ghostClass: "sortable-ghost"
  265.         sort: false
  266.         animation: 150, 
  267.         group: { 
  268.           name"person"
  269.           pull: true
  270.           put: true
  271.         }, 
  272.         setData: function ( 
  273.           /** DataTransfer */ dataTransfer, 
  274.           /** HTMLElement*/ dragEl 
  275.         ) { 
  276.           dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  277.         }, 
  278.         onStart: () => { 
  279.           this.useInitData("newGuestList", this.guestData); // 初始化 
  280.         }, 
  281.         onAdd: (evt) => { 
  282.           this.useReduction(0); 
  283.           if (this[`select${this.fromItem.split("new")[1]}`].length === 0) { 
  284.             this.useAddNewData(evt, "newGuestList", this.guestData); 
  285.           } else { 
  286.             this.useAddsNewData(evt, "newGuestList", this.guestData); 
  287.           } 
  288.         }, 
  289.         onEnd: (ev) => { 
  290.           if (this[`select${this.fromItem.split("new")[1]}`].length !== 0) { 
  291.             this.useReduction(0); 
  292.             if (ev.to.outerText.indexOf("管理员") !== -1) { 
  293.               this.$nextTick(() => { 
  294.                 this.newGuestList = deepClone(this.guestData); 
  295.                 const data = deepClone(this.guestData); 
  296.                 this.guestData = data; 
  297.               }); 
  298.             } else { 
  299.               const data = deepClone(this.guestData); 
  300.               this.newGuestList = deepClone(this.guestData); 
  301.               this.guestData = []; 
  302.               this.$nextTick(() => { 
  303.                 this.guestData = data; 
  304.               }); 
  305.             } 
  306.           } else { 
  307.             this.$nextTick(() => { 
  308.               this.guestData = this.newGuestList; 
  309.             }); 
  310.           } 
  311.         }, 
  312.       }); 
  313.     }, 
  314.     // 拖拽操作员 
  315.     sortOperator() { 
  316.       const el = this.$refs.operatorData.$el.querySelectorAll( 
  317.         ".el-table__body-wrapper > table> tbody" 
  318.       )[0]; 
  319.       Sortable.create(el, { 
  320.         ghostClass: "sortable-ghost"
  321.         sort: false
  322.         animation: 150, 
  323.         group: { 
  324.           name"person"
  325.           pull: true
  326.           put: true
  327.         }, 
  328.         setData: function ( 
  329.           /** DataTransfer */ dataTransfer, 
  330.           /** HTMLElement*/ dragEl 
  331.         ) { 
  332.           dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  333.         }, 
  334.         onStart: () => { 
  335.           this.useInitData("newOperatorList", this.operatorData); // 初始化 
  336.         }, 
  337.         onAdd: (evt) => { 
  338.           this.useReduction(1); 
  339.           if (this[`select${this.fromItem.split("new")[1]}`].length === 0) { 
  340.             this.useAddNewData(evt, "newOperatorList", this.operatorData); 
  341.           } else { 
  342.             this.useAddsNewData(evt, "newOperatorList", this.operatorData); 
  343.           } 
  344.         }, 
  345.         onEnd: (ev) => { 
  346.           if (this[`select${this.fromItem.split("new")[1]}`].length !== 0) { 
  347.             this.useReduction(1); 
  348.             if (ev.to.outerText.indexOf("管理员") !== -1) { 
  349.               this.$nextTick(() => { 
  350.                 this.newOperatorList = deepClone(this.operatorData); 
  351.                 const data = deepClone(this.operatorData); 
  352.                 this.operatorData = data; 
  353.               }); 
  354.             } else { 
  355.               const data = deepClone(this.operatorData); 
  356.               this.newOperatorList = deepClone(this.operatorData); 
  357.               this.operatorData = []; 
  358.               this.$nextTick(() => { 
  359.                 this.operatorData = data; 
  360.               }); 
  361.             } 
  362.           } else { 
  363.             this.$nextTick(() => { 
  364.               this.operatorData = this.newOperatorList; 
  365.             }); 
  366.           } 
  367.         }, 
  368.       }); 
  369.     }, 
  370.     // 拖拽电工 
  371.     sortElectrician() { 
  372.       const el = this.$refs.electricianData.$el.querySelectorAll( 
  373.         ".el-table__body-wrapper > table> tbody" 
  374.       )[0]; 
  375.       Sortable.create(el, { 
  376.         ghostClass: "sortable-ghost"
  377.         sort: false
  378.         animation: 150, 
  379.         group: { 
  380.           name"person"
  381.           pull: true
  382.           put: true
  383.         }, 
  384.         setData: function ( 
  385.           /** DataTransfer */ dataTransfer, 
  386.           /** HTMLElement*/ dragEl 
  387.         ) { 
  388.           dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  389.         }, 
  390.         onStart: () => { 
  391.           this.useInitData("newElectricianList", this.electricianData); // 初始化 
  392.         }, 
  393.         onAdd: (evt) => { 
  394.           this.useReduction(2); 
  395.           if (this[`select${this.fromItem.split("new")[1]}`].length === 0) { 
  396.             this.useAddNewData(evt, "newElectricianList", this.electricianData); 
  397.           } else { 
  398.             this.useAddsNewData( 
  399.               evt, 
  400.               "newElectricianList"
  401.               this.electricianData 
  402.             ); 
  403.           } 
  404.         }, 
  405.         onEnd: (ev) => { 
  406.           if (this[`select${this.fromItem.split("new")[1]}`].length !== 0) { 
  407.             this.useReduction(2); 
  408.             if (ev.to.outerText.indexOf("管理员") !== -1) { 
  409.               this.$nextTick(() => { 
  410.                 this.newElectricianList = deepClone(this.electricianData); 
  411.                 const data = deepClone(this.electricianData); 
  412.                 this.electricianData = data; 
  413.               }); 
  414.             } else { 
  415.               const data = deepClone(this.electricianData); 
  416.               this.newElectricianList = deepClone(this.electricianData); 
  417.               this.electricianData = []; 
  418.               this.$nextTick(() => { 
  419.                 this.electricianData = data; 
  420.               }); 
  421.             } 
  422.           } else { 
  423.             this.$nextTick(() => { 
  424.               this.electricianData = this.newElectricianList; 
  425.             }); 
  426.           } 
  427.         }, 
  428.       }); 
  429.     }, 
  430.     // 拖拽管理员 
  431.     sortManager() { 
  432.       const el = this.$refs.managerData.$el.querySelectorAll( 
  433.         ".el-table__body-wrapper > table > tbody" 
  434.       )[0]; 
  435.       Sortable.create(el, { 
  436.         ghostClass: "sortable-ghost"
  437.         sort: false
  438.         animation: 150, 
  439.         group: { 
  440.           name"person"
  441.           pull: false
  442.           put: true
  443.         }, 
  444.         setData: function ( 
  445.           /** DataTransfer */ dataTransfer, 
  446.           /** HTMLElement*/ dragEl 
  447.         ) { 
  448.           dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  449.         }, 
  450.         onAdd: (evt) => { 
  451.           console.log(evt) 
  452.           switch (this.fromItem) { 
  453.             case "newGuestList"
  454.               { 
  455.                 const data = deepClone(this.guestData); 
  456.                 this.guestData = []; 
  457.                 this.$nextTick(() => { 
  458.                   this.guestData = data; 
  459.                 }); 
  460.               } 
  461.               break; 
  462.             case "newOperatorList"
  463.               { 
  464.                 const data = deepClone(this.operatorData); 
  465.                 this.operatorData = []; 
  466.                 this.$nextTick(() => { 
  467.                   this.operatorData = data; 
  468.                 }); 
  469.               } 
  470.               break; 
  471.             case "newElectricianList"
  472.               { 
  473.                 const data = deepClone(this.electricianData); 
  474.                 this.electricianData = []; 
  475.                 this.$nextTick(() => { 
  476.                   this.electricianData = data; 
  477.                 }); 
  478.               } 
  479.               break; 
  480.             default
  481.               break; 
  482.           } 
  483.           if (this[`select${this.fromItem.split("new")[1]}`].length < 2) { 
  484.             this.managerOldIndex = evt.oldIndex; 
  485.             this.passwordView = true
  486.           } else { 
  487.             this.$message({ 
  488.               message: "批量失败!"
  489.               type: "warning"
  490.             }); 
  491.           } 
  492.           this.$refs.managerData.$el 
  493.             .querySelectorAll(".el-table__body-wrapper > table > tbody")[0] 
  494.             .removeChild(evt.item); 
  495.         }, 
  496.       }); 
  497.     }, 
  498.   }, 
  499. }; 
  500. </script> 

我们这里使用的拖拽插件是sortablejs,功能非常强大。我这里就简单介绍下它的使用。

  1. Sortable.create(el,{}) 

这里,需要给Sortable对象下的create方法传入两个参数,第一个参数是el节点,这个节点是定义可拖拽的每一项,如:

  1. const el = this.$refs.guestData.$el.querySelectorAll( 
  2.   ".el-table__body-wrapper > table> tbody" 
  3. )[0]; 

这里的意思就是游客表格中的表格项。

第二个参数是可配置参数,可以定义配置属性与方法。详情参数与方法可以参照中文官网:http://www.sortablejs.com/。

下面,我们将分层来讲解逻辑实现。data方法中返回的对象我们看到只是初始化了一些数据,这里先不过多阐述。然后到了watch属性,它监听了data方法中返回的对象的passwordView属性,并对应的监听方法是watchPasswordView。

我们往下面methods属性中找到,就是简单地对密码框中的内容每次初始化(置空)。

  1. // 密码框置空 
  2. watchPasswordView(val) { 
  3.   if (!val) { 
  4.     this.password = ""
  5.   } 
  6. }, 

然后,我们进入了created方法,我们主要做了两件事,一件事是定义了一个静态对象(没有定义在data方法中,所以没有做响应式处理,为了性能优化),另一件事是获取表格数据。

  1. created() { 
  2. // 定义静态数据 
  3. this.obj = { 
  4.   newGuestList: ["guestData", 0], 
  5.   newManagerList: ["managerData", 3], 
  6.   newOperatorList: ["operatorData", 1], 
  7.   newElectricianList: ["electricianData", 2], 
  8. }; 
  9. // 获取表格数据 
  10. this.guestData = tableData.guestData; 
  11. this.managerData = tableData.managerData; 
  12. this.electricianData = tableData.electricianData; 
  13. this.operatorData = tableData.operatorData; 
  14. }, 

然后,我们进入mounted方法,我们看到方法中调用了这几个拖拽表格方法,为什么会放在mounted方法中呢?是因为要想使用拖拽,必须等到实例被挂载后调用。

最后,我们将进入methods属性,这里定义了很多方法,下面我们还是分功能部分开始分析。

  1. mounted() { 
  2.     this.sortGuest(); 
  3.     this.sortOperator(); 
  4.     this.sortElectrician(); 
  5.     this.sortManager(); 

这几个方法主要是点击对应表格项进行勾选。

  1. // 选择游客 
  2. guestDataSelect(row) { 
  3.   row.flag = !row.flag; 
  4.   this.$refs.guestData.toggleRowSelection(row, row.flag); 
  5. }, 
  6. // 选择操作员 
  7. operatorDataSelect(row) { 
  8.   row.flag = !row.flag; 
  9.   this.$refs.operatorData.toggleRowSelection(row, row.flag); 
  10. }, 
  11. // 选择电工 
  12. electricianDataSelect(row) { 
  13.   row.flag = !row.flag; 
  14.   this.$refs.electricianData.toggleRowSelection(row, row.flag); 

将选择的表格项数据存起来。

  1. // 监听游客表格选择 
  2. selectionGuestChange(val) { 
  3.   this.selectGuestList = val; 
  4. }, 
  5. // 监听操作员表格选择 
  6. selectionOperatorChange(val) { 
  7.   this.selectOperatorList = val; 
  8. }, 
  9. // 监听电工表格选择 
  10. selectionElectricianChange(val) { 
  11.   this.selectElectricianList = val; 

为每一表格项定义一个唯一的key。

  1. // 获取userId 
  2. getUserId(row) { 
  3.   return row.userId; 
  4. }, 

我们进入关键部分,也就是拖拽。是不是看起来代码特别多,其实这块还可以优化,但是为了更容易分辨,先这样。我们看到这几个方法中都有一个相同的部分,就是先定义el变量,然后执行Sortable.create(el, {})方法,另外,Sortable.create()的第二个参数中,都有onStart()、onAdd()、onEnd()这几个方法。

  1. // 拖拽游客 
  2. sortGuest() { 
  3.   const el = this.$refs.guestData.$el.querySelectorAll( 
  4.     ".el-table__body-wrapper > table> tbody" 
  5.   )[0]; 
  6.   Sortable.create(el, { 
  7.     ghostClass: "sortable-ghost"
  8.     sort: false
  9.     animation: 150, 
  10.     group: { 
  11.       name"person"
  12.       pull: true
  13.       put: true
  14.     }, 
  15.     setData: function ( 
  16.       /** DataTransfer */ dataTransfer, 
  17.       /** HTMLElement*/ dragEl 
  18.     ) { 
  19.       dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  20.     }, 
  21.     onStart: () => { 
  22.       this.useInitData("newGuestList", this.guestData); // 初始化 
  23.     }, 
  24.     onAdd: (evt) => { 
  25.       this.useReduction(0); 
  26.       if (this[`select${this.fromItem.split("new")[1]}`].length === 0) { 
  27.         this.useAddNewData(evt, "newGuestList", this.guestData); 
  28.       } else { 
  29.         this.useAddsNewData(evt, "newGuestList", this.guestData); 
  30.       } 
  31.     }, 
  32.     onEnd: (ev) => { 
  33.       if (this[`select${this.fromItem.split("new")[1]}`].length !== 0) { 
  34.         this.useReduction(0); 
  35.         if (ev.to.outerText.indexOf("管理员") !== -1) { 
  36.           this.$nextTick(() => { 
  37.             this.newGuestList = deepClone(this.guestData); 
  38.             const data = deepClone(this.guestData); 
  39.             this.guestData = data; 
  40.           }); 
  41.         } else { 
  42.           const data = deepClone(this.guestData); 
  43.           this.newGuestList = deepClone(this.guestData); 
  44.           this.guestData = []; 
  45.           this.$nextTick(() => { 
  46.             this.guestData = data; 
  47.           }); 
  48.         } 
  49.       } else { 
  50.         this.$nextTick(() => { 
  51.           this.guestData = this.newGuestList; 
  52.         }); 
  53.       } 
  54.     }, 
  55.   }); 
  56. }, 
  57. // 拖拽操作员 
  58. sortOperator() { 
  59.   const el = this.$refs.operatorData.$el.querySelectorAll( 
  60.     ".el-table__body-wrapper > table> tbody" 
  61.   )[0]; 
  62.   Sortable.create(el, { 
  63.     ghostClass: "sortable-ghost"
  64.     sort: false
  65.     animation: 150, 
  66.     group: { 
  67.       name"person"
  68.       pull: true
  69.       put: true
  70.     }, 
  71.     setData: function ( 
  72.       /** DataTransfer */ dataTransfer, 
  73.       /** HTMLElement*/ dragEl 
  74.     ) { 
  75.       dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  76.     }, 
  77.     onStart: () => { 
  78.       this.useInitData("newOperatorList", this.operatorData); // 初始化 
  79.     }, 
  80.     onAdd: (evt) => { 
  81.       this.useReduction(1); 
  82.       if (this[`select${this.fromItem.split("new")[1]}`].length === 0) { 
  83.         this.useAddNewData(evt, "newOperatorList", this.operatorData); 
  84.       } else { 
  85.         this.useAddsNewData(evt, "newOperatorList", this.operatorData); 
  86.       } 
  87.     }, 
  88.     onEnd: (ev) => { 
  89.       if (this[`select${this.fromItem.split("new")[1]}`].length !== 0) { 
  90.         this.useReduction(1); 
  91.         if (ev.to.outerText.indexOf("管理员") !== -1) { 
  92.           this.$nextTick(() => { 
  93.             this.newOperatorList = deepClone(this.operatorData); 
  94.             const data = deepClone(this.operatorData); 
  95.             this.operatorData = data; 
  96.           }); 
  97.         } else { 
  98.           const data = deepClone(this.operatorData); 
  99.           this.newOperatorList = deepClone(this.operatorData); 
  100.           this.operatorData = []; 
  101.           this.$nextTick(() => { 
  102.             this.operatorData = data; 
  103.           }); 
  104.         } 
  105.       } else { 
  106.         this.$nextTick(() => { 
  107.           this.operatorData = this.newOperatorList; 
  108.         }); 
  109.       } 
  110.     }, 
  111.   }); 
  112. }, 
  113. // 拖拽电工 
  114. sortElectrician() { 
  115.   const el = this.$refs.electricianData.$el.querySelectorAll( 
  116.     ".el-table__body-wrapper > table> tbody" 
  117.   )[0]; 
  118.   Sortable.create(el, { 
  119.     ghostClass: "sortable-ghost"
  120.     sort: false
  121.     animation: 150, 
  122.     group: { 
  123.       name"person"
  124.       pull: true
  125.       put: true
  126.     }, 
  127.     setData: function ( 
  128.       /** DataTransfer */ dataTransfer, 
  129.       /** HTMLElement*/ dragEl 
  130.     ) { 
  131.       dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  132.     }, 
  133.     onStart: () => { 
  134.       this.useInitData("newElectricianList", this.electricianData); // 初始化 
  135.     }, 
  136.     onAdd: (evt) => { 
  137.       this.useReduction(2); 
  138.       if (this[`select${this.fromItem.split("new")[1]}`].length === 0) { 
  139.         this.useAddNewData(evt, "newElectricianList", this.electricianData); 
  140.       } else { 
  141.         this.useAddsNewData( 
  142.           evt, 
  143.           "newElectricianList"
  144.           this.electricianData 
  145.         ); 
  146.       } 
  147.     }, 
  148.     onEnd: (ev) => { 
  149.       if (this[`select${this.fromItem.split("new")[1]}`].length !== 0) { 
  150.         this.useReduction(2); 
  151.         if (ev.to.outerText.indexOf("管理员") !== -1) { 
  152.           this.$nextTick(() => { 
  153.             this.newElectricianList = deepClone(this.electricianData); 
  154.             const data = deepClone(this.electricianData); 
  155.             this.electricianData = data; 
  156.           }); 
  157.         } else { 
  158.           const data = deepClone(this.electricianData); 
  159.           this.newElectricianList = deepClone(this.electricianData); 
  160.           this.electricianData = []; 
  161.           this.$nextTick(() => { 
  162.             this.electricianData = data; 
  163.           }); 
  164.         } 
  165.       } else { 
  166.         this.$nextTick(() => { 
  167.           this.electricianData = this.newElectricianList; 
  168.         }); 
  169.       } 
  170.     }, 
  171.   }); 
  172. }, 
  173. // 拖拽管理员 
  174. sortManager() { 
  175.   const el = this.$refs.managerData.$el.querySelectorAll( 
  176.     ".el-table__body-wrapper > table > tbody" 
  177.   )[0]; 
  178.   Sortable.create(el, { 
  179.     ghostClass: "sortable-ghost"
  180.     sort: false
  181.     animation: 150, 
  182.     group: { 
  183.       name"person"
  184.       pull: false
  185.       put: true
  186.     }, 
  187.     setData: function ( 
  188.       /** DataTransfer */ dataTransfer, 
  189.       /** HTMLElement*/ dragEl 
  190.     ) { 
  191.       dataTransfer.setData("Text", dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent 
  192.     }, 
  193.     onAdd: (evt) => { 
  194.       console.log(evt) 
  195.       switch (this.fromItem) { 
  196.         case "newGuestList"
  197.           { 
  198.             const data = deepClone(this.guestData); 
  199.             this.guestData = []; 
  200.             this.$nextTick(() => { 
  201.               this.guestData = data; 
  202.             }); 
  203.           } 
  204.           break; 
  205.         case "newOperatorList"
  206.           { 
  207.             const data = deepClone(this.operatorData); 
  208.             this.operatorData = []; 
  209.             this.$nextTick(() => { 
  210.               this.operatorData = data; 
  211.             }); 
  212.           } 
  213.           break; 
  214.         case "newElectricianList"
  215.           { 
  216.             const data = deepClone(this.electricianData); 
  217.             this.electricianData = []; 
  218.             this.$nextTick(() => { 
  219.               this.electricianData = data; 
  220.             }); 
  221.           } 
  222.           break; 
  223.         default
  224.           break; 
  225.       } 
  226.       if (this[`select${this.fromItem.split("new")[1]}`].length < 2) { 
  227.         this.managerOldIndex = evt.oldIndex; 
  228.         this.passwordView = true
  229.       } else { 
  230.         this.$message({ 
  231.           message: "批量失败!"
  232.           type: "warning"
  233.         }); 
  234.       } 
  235.       this.$refs.managerData.$el 
  236.         .querySelectorAll(".el-table__body-wrapper > table > tbody")[0] 
  237.         .removeChild(evt.item); 
  238.     }, 
  239.   }); 
  240. }, 

因为onStart()、onAdd()、onEnd()这几个方法在sortGuest()以及其与几个拖拽方法中逻辑都差不多,所以我们就单独摘出sortGuest()方法进行分析下。

首先,可以看到onStart()方法中调用了this.useInitData(),意思就是当你开始拖拽的时候调用this.useInitData()方法,这个方法第一个参数传一个字符串,第二个参数传一个数组,即当前表格数据。这个方法做了两个工作,一是定义一个开始拖拽时记录当前表格的标识,二是将当前表格的数据克隆到新数组中。

  1. // 封装初始化数据 
  2. useInitData(fromItem, oldData) { 
  3.   this.fromItem = fromItem; 
  4.   this[fromItem] = this.initStatus[fromItem] 
  5.     ? deepClone(oldData) 
  6.     : deepClone(this[fromItem]); 
  7.   this.initStatus[fromItem] = false

然后,我们再分析onAdd()方法,它的意思是当被拖入添加到当前表格时触发。这个方法中做了两项工作,一是调用了useReduction方法,二是根据旧表格项是否有选择数据来调用不同的方法。

  1. onAdd: (evt) => { 
  2.     this.useReduction(0); 
  3.     if (this[`select${this.fromItem.split("new")[1]}`].length === 0) { 
  4.       this.useAddNewData(evt, "newGuestList", this.guestData); 
  5.     } else { 
  6.       this.useAddsNewData(evt, "newGuestList", this.guestData); 
  7.     } 

以下是useReduction方法。

  1. // 还原初始状态 
  2. useReduction(i) { 
  3.   const arr = [ 
  4.     { 
  5.       data: "guestData"
  6.       sletData: "selectGuestList"
  7.     }, 
  8.     { 
  9.       data: "operatorData"
  10.       sletData: "selectOperatorList"
  11.     }, 
  12.     { 
  13.       data: "electricianData"
  14.       sletData: "selectElectricianList"
  15.     }, 
  16.   ]; 
  17.  
  18.   this.$refs[arr[i].data].clearSelection(); // 将选中的勾选框置空 
  19.   this[arr[i].sletData] = []; // 将选择数据置空 

接着,我们来看下useAddNewData()方法,这个方法是封装了一个拖拽添加单项的方法。第一个参数是onAdd()方法中的第一个参数,第二个参数是一个字符串,即新数据的标识,第三个参数是当前被添加的表格数据对象。

首先,我们取到需要添加的表格项,然后使用this.$loading()方法首先一个数据加载动画。更新新旧数据,将当前项添加到当前表格,并且删除旧表格中的数据,使用removeChild方法删除页面元素。

useAddsNewData方法同理,只不过遍历选择数据。

  1. // 封装添加数据 
  2. useAddNewData(evt, newData, oldData) { 
  3.   const item = this[this.fromItem][evt.oldIndex]; // 添加项 
  4.  
  5.   const loading = this.$loading({ 
  6.     lock: true
  7.     text: "加载中"
  8.     spinner: "el-icon-loading"
  9.     background: "rgba(0, 0, 0, 0.7)"
  10.   }); 
  11.  
  12.   setTimeout(() => { 
  13.     loading.close(); 
  14.     this.$message({ 
  15.       message: "拖拽成功!"
  16.       type: "success"
  17.     }); 
  18.   }, 1000); 
  19.  
  20.   this[newData] = this.initStatus[newData] 
  21.     ? deepClone(oldData) 
  22.     : deepClone(this[newData]); 
  23.   this.initStatus[newData] = false
  24.   oldData.push(item); 
  25.   this[newData].push(item); 
  26.   this[this.fromItem].splice(evt.oldIndex, 1); 
  27.   this.$refs[this.obj[newData][0]].$el 
  28.     .querySelectorAll(".el-table__body-wrapper > table > tbody")[0] 
  29.     .removeChild(evt.item); 
  30. }, 
  31.  
  32. // 封装添加(多)数据 
  33. useAddsNewData(evt, newData, oldData) { 
  34.   const arr = []; 
  35.   for ( 
  36.     let index = 0; 
  37.     index < this[`select${this.fromItem.split("new")[1]}`].length; 
  38.     index++ 
  39.   ) { 
  40.     const element = this[`select${this.fromItem.split("new")[1]}`][index]; 
  41.     arr.push(element.userId); 
  42.   } 
  43.   const loading = this.$loading( 
  44.     lock: true
  45.     text: "加载中"
  46.     spinner: "el-icon-loading"
  47.     background: "rgba(0, 0, 0, 0.7)"
  48.   }); 
  49.  
  50.   setTimeout(() => { 
  51.     loading.close(); 
  52.     this.$message({ 
  53.       message: "批量拖拽成功!"
  54.       type: "success"
  55.     }); 
  56.   }, 1000); 
  57.   this[newData] = this.initStatus[newData] 
  58.     ? deepClone(oldData) 
  59.     : deepClone(this[newData]); 
  60.   this.initStatus[newData] = false
  61.   this[newData].push(...this[`select${this.fromItem.split("new")[1]}`]); 
  62.   this[this.obj[newData][0]].push( 
  63.     ...this[`select${this.fromItem.split("new")[1]}`] 
  64.   ); 
  65.   this.useDel( 
  66.     this[`select${this.fromItem.split("new")[1]}`], 
  67.     this[this.obj[this.fromItem][0]] 
  68.   ); 
  69.   this.$refs[this.obj[newData][0]].$el 
  70.     .querySelectorAll(".el-table__body-wrapper > table > tbody")[0] 
  71.     .removeChild(evt.item); 
  72. }, 

我们在useAddsNewData方法中有一个方法是useDel方法,这个方法的作用是批量删除数组中的元素,这里旧数据删除指定项。

  1. // 批量删除(数组) 
  2. useDel(data, currentData) { 
  3.   for (let i = 0; i < data.length; i++) { 
  4.     const element = data[i]; 
  5.     for (let j = 0; j < currentData.length; j++) { 
  6.       const item = currentData[j]; 
  7.       if (item === element) { 
  8.         currentData.splice(j, 1); 
  9.       } 
  10.     } 
  11.   } 
  12. }, 

我们还有最后一个方法okChangeManager(),这个方法最外层是判断密码是否为空,我这里简化了逻辑,这里本来是需要调用接口来,但是为了好理解,所以先忽视这部分。同样,我们需要获取被添加项,因为添加项只能是一个,所以这地方我们直接看条件允许的情况下。我们需要知道被添加项添加到管理员数据表格中,原先的数据会被移到游客表格,并且被添加项从原始表格数据中删除。

  1. // 确定拖拽到管理员 
  2. okChangeManager() { 
  3.   if (this.password.trim().length > 0) { 
  4.     const item = this[this.fromItem][this.managerOldIndex]; // 添加项 
  5.     if (item) { 
  6.       this.newManagerList = this.initStatus.newManagerList 
  7.         ? deepClone(this.managerData) 
  8.         : deepClone(this.newManagerList); 
  9.       this.newGuestList = this.initStatus.newGuestList 
  10.         ? deepClone(this.guestData) 
  11.         : deepClone(this.newGuestList); 
  12.       this.initStatus.newGuestList = false
  13.       this.initStatus.newManagerList = false
  14.       this.newManagerList = [item]; 
  15.       this[this.fromItem].splice(this.managerOldIndex, 1); 
  16.       this.initStatus[this.fromItem] = false
  17.  
  18.       if (this.managerData[0]) { 
  19.         const obj = deepClone(this.managerData[0]); 
  20.         this.newGuestList.push(obj); 
  21.         this.guestData.push(obj); 
  22.       } 
  23.       this.managerData = [item]; 
  24.       switch (this.fromItem) { 
  25.         case "newGuestList"
  26.           this.guestData = deepClone(this.newGuestList); 
  27.           break; 
  28.         case "newOperatorList"
  29.           this.operatorData = deepClone(this.newOperatorList); 
  30.           break; 
  31.         case "newElectricianList"
  32.           this.electricianData = deepClone(this.newElectricianList); 
  33.           break; 
  34.         default
  35.           break; 
  36.       } 
  37.       this.$message({ 
  38.         message: "拖拽成功!"
  39.         type: "success"
  40.       }); 
  41.       this.password = ""
  42.       this.passwordView = false
  43.     } else { 
  44.       this.$message({ 
  45.         message: "拖拽失败"
  46.         type: "warning"
  47.       }); 
  48.       this.password = ""
  49.       this.passwordView = false
  50.     } 
  51.   } else { 
  52.     this.$message({ 
  53.       message: "请输入密码"
  54.       type: "warning"
  55.     }); 
  56.   } 
  57. }, 

3. 样式代码

  1. <style scoped> 
  2. .top-name { 
  3.   padding: 10px; 
  4.   background: #333; 
  5.   font-size: 14px; 
  6.   position: relative
  7. .top-name > p { 
  8.   color: #00a7ff; 
  9.   text-align: center; 
  10.   font-size: 16px; 
  11. .main-box { 
  12.   display: flex; 
  13.   justify-content: space-between
  14. .main-l,.main-r { 
  15.   width: 48%; 
  16.   position: relative
  17. .table-b { 
  18.   position: relative
  19. .isdel { 
  20.   text-align: center; 
  21.   font-size: 18px; 
  22.   color: #fff; 
  23. .top-box { 
  24.   display: flex; 
  25.   height: 30px; 
  26.   justify-content: space-between
  27.   align-items: center; 
  28. .top-count { 
  29.   color: #fff; 
  30.   font-size: 14px; 
  31.   margin-left:10px ; 
  32. .utable-box { 
  33.   overflow: auto; 
  34.   height: 700px; 
  35. .table-box { 
  36.   overflow: auto; 
  37.   height: 230px; 
  38.   margin-bottom: 10px; 
  39.   border: 1px solid #333; 
  40. </style> 

样式在这里就不过多的分析了。

结语

如果想看下完整代码,看下真实操作效果,可以访问下方的源码地址:

https://github.com/maomincoding/drag-tables

原文链接:https://mp.weixin.qq.com/s/55w-2koItt1HHvBfsx2tZw