首页 > 其他分享 >vue2 中 el-table 实现树形列表,支持增删改等操作

vue2 中 el-table 实现树形列表,支持增删改等操作

时间:2023-12-14 15:33:19浏览次数:31  
标签:改等 el tableData 节点 item vue2 child id row

  1. 需求场景:el-table构造一个树形列表,支持新增节点,删除,修改等操作。
  2. 实现效果
  3. 思路

    一般的el-table 增删改,我们都很熟悉;关键在于实现一个纯前端的树形列表,最终再调接口存列表数据。

         树形el-table,需要设置 row-key,一般为 id,所以每新增一条数据,都必须有id。需要一个生成id的方法:

// 生成id 时间戳 + 随机数
generateId() { return `id_${new Date().getTime()}${Math.floor(Math.random() * 10000)}` }

  有树形结构,就得有父子关系,因此除了id还需要有parentId。根节点parentId,此处定义为"0"。

  这里方便看效果,内置了一条tableData数据,然后再构造一个基础树形数据:

  tableData:

// 数据示例
      tableData: [
        {
          key: 'name',
          type: 'string',
          child: null
        },
        {
          key: 'age',
          type: 'integer',
          child: null
        },
        {
          key: 'response',
          type: 'object',
          child: [
            {
              key: 'childrenone',
              type: 'string',
              child: null
            },
            {
              key: 'childrentwo',
              type: 'boolean',
              child: null
            }
          ]
        },
        {
          key: 'address',
          type: 'string',
          child: null
        }
      ]
View Code

构造基础树形数据,如果自己实现,可以忽略这一步。

// 数据准备 生成 id 和 parentId
    handleTableDataFormat(data) {
      const tableFormat = (tableData, parentId) => {
        tableData.forEach((item) => {
          item.isEdit = false
          item.parentId = parentId || '0'
          item.id = this.generateId()
          if (item.child && item.child.length > 0) {
            tableFormat(item.child, item.id)
          }
        })
      }

      tableFormat(data)
      console.log('Format tableData', data)
      return data
    }
View Code

  这里设定,列表里有数据类型列,如果当前为object类型,就可以添加子节点。

  新增,修改,删除中,需要先处理新增数据的情况,有3种:新增根节点数据、新增子节点数据、新增同级节点数据。

  •        新增根节点

       直接Array.push()

  •   新增子节点

       先找到当前节点,然后再判断是否存在子节点,如果存在,直接在当前行的child上添加一条,如果不存在,则直接给child赋值。

  •   新增同级节点

      找到当前节点的父节点,然后在父节点的child属性上追加一条。

  •   删除节点

  如果是根节点,可以直接删除;如果是子节点,则需要先找到父节点,然后再从父节点child中删除当前的节点。

// 删除当前节点及对应子节点数据
    onDelete(row) {
      const msg =
        '<div><span style="color: #F56C6C">删除后将不可恢复</span>,你还要继续吗?</div>'
      this.$confirm(msg, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        dangerouslyUseHTMLString: true,
        type: 'warning'
      })
        .then(() => {
          const { parentId, id } = row
          // 根节点直接删除
          if (parentId === '0') {
            const delIndex = this.tableData.findIndex((item) => item.id === id)
            this.tableData.splice(delIndex, 1)
          } else {
            // 找到父节点,通过父节点删除
            let parentRow = {}
            const findRow = (data) => {
              data.forEach((item) => {
                if (item.id === parentId) {
                  parentRow = { ...item }
                }
                if (item.child && item.child.length) {
                  findRow(item.child)
                }
              })
            }
            findRow(this.tableData)

            const { child } = parentRow

            const delIndex = child.findIndex((item) => item.id === id)

            child.splice(delIndex, 1)
          }
        })
        .catch(() => {})
    }
View Code
  •       编辑节点

       编辑节点,比较好实现,直接使用$set 重新赋值即可。

 

下面是完整代码:

  1 <template>
  2   <div class="custom-tree-table">
  3     <el-table
  4       ref="tableDataRef"
  5       :data="tableData"
  6       max-height="400"
  7       row-key="id"
  8       border
  9       :tree-props="{ children: 'child' }"
 10       default-expand-all
 11     >
 12       <el-table-column width="55" align="center" type="index" label="序号" />
 13       <el-table-column label="参数名" prop="key" min-width="200">
 14         <template #default="{ row }">
 15           <el-input v-if="row.isEdit" v-model="row.key" placeholder="请输入" />
 16           <span v-else>{{ row.key }}</span>
 17         </template>
 18       </el-table-column>
 19       <el-table-column label="数据类型" prop="type" min-width="200">
 20         <template #default="{ row }">
 21           <el-select
 22             v-if="row.isEdit"
 23             v-model="row.type"
 24             filterable
 25             clearable
 26             placeholder="请选择"
 27             style="width: 100%"
 28             @change="handleDataTypeChange($event, row)"
 29           >
 30             <el-option
 31               v-for="item in source.dataTypeOptions"
 32               :key="item.value"
 33               :value="item.value"
 34               :label="item.label"
 35             />
 36           </el-select>
 37           <span v-else>{{ row.type }}</span>
 38         </template>
 39       </el-table-column>
 40       <el-table-column label="操作" min-width="100">
 41         <template slot="header">
 42           <el-tooltip
 43             :hide-after="500"
 44             class="item"
 45             effect="dark"
 46             content="添加根节点"
 47             placement="top"
 48           >
 49             <el-button
 50               type="text"
 51               icon="el-icon-circle-plus-outline"
 52               @click="onAddRoot"
 53             />
 54           </el-tooltip>
 55         </template>
 56         <template #default="{ row, $index }">
 57           <el-tooltip
 58             :hide-after="hideAfter"
 59             :open-delay="openDelay"
 60             effect="dark"
 61             content="添加"
 62             placement="top"
 63           >
 64             <el-button
 65               type="text"
 66               icon="el-icon-plus"
 67               @click="onAddSibling(row, $index)"
 68             />
 69           </el-tooltip>
 70 
 71           <el-tooltip
 72             v-if="row.type === 'object'"
 73             :hide-after="hideAfter"
 74             :open-delay="openDelay"
 75             effect="dark"
 76             content="添加子节点"
 77             placement="top"
 78           >
 79             <el-button
 80               type="text"
 81               icon="el-icon-circle-plus-outline"
 82               @click="onAddChild(row, $index)"
 83             />
 84           </el-tooltip>
 85 
 86           <el-tooltip
 87             v-if="!row.isEdit"
 88             :hide-after="hideAfter"
 89             :open-delay="openDelay"
 90             effect="dark"
 91             content="编辑"
 92             placement="top"
 93           >
 94             <el-button
 95               type="text"
 96               icon="el-icon-edit"
 97               @click="onEdit(row, $index)"
 98             />
 99           </el-tooltip>
100 
101           <el-tooltip
102             v-if="row.isEdit"
103             :hide-after="hideAfter"
104             :open-delay="openDelay"
105             effect="dark"
106             content="保存"
107             placement="top"
108           >
109             <el-button
110               type="text"
111               icon="el-icon-circle-check"
112               @click="onSave(row, $index)"
113             />
114           </el-tooltip>
115 
116           <el-tooltip
117             :hide-after="hideAfter"
118             :open-delay="openDelay"
119             effect="dark"
120             content="删除"
121             placement="top"
122           >
123             <el-button
124               type="text"
125               icon="el-icon-delete"
126               @click="onDelete(row, $index)"
127             />
128           </el-tooltip>
129         </template>
130       </el-table-column>
131     </el-table>
132   </div>
133 </template>
134 
135 <script>
136 export default {
137   name: 'CustomTreeTable',
138   data() {
139     return {
140       source: {
141         dataTypeOptions: [
142           { label: 'Array', value: 'array' },
143           { label: 'String', value: 'string' },
144           { label: 'Boolean', value: 'boolean' },
145           { label: 'Object', value: 'object' },
146           { label: 'Number', value: 'number' }
147         ]
148       },
149       hideAfter: 1500,
150       openDelay: 500,
151       // 数据示例
152       tableData: [
153         {
154           key: 'name',
155           type: 'string',
156           child: null
157         },
158         {
159           key: 'age',
160           type: 'integer',
161           child: null
162         },
163         {
164           key: 'response',
165           type: 'object',
166           child: [
167             {
168               key: 'childrenone',
169               type: 'string',
170               child: null
171             },
172             {
173               key: 'childrentwo',
174               type: 'boolean',
175               child: null
176             }
177           ]
178         },
179         {
180           key: 'address',
181           type: 'string',
182           child: null
183         }
184       ]
185     }
186   },
187   created() {
188     this.tableData = this.handleTableDataFormat(this.tableData)
189   },
190   methods: {
191     // 数据准备 生成 id 和 parentId
192     handleTableDataFormat(data) {
193       const tableFormat = (tableData, parentId) => {
194         tableData.forEach((item) => {
195           item.isEdit = false
196           item.parentId = parentId || '0'
197           item.id = this.generateId()
198           if (item.child && item.child.length > 0) {
199             tableFormat(item.child, item.id)
200           }
201         })
202       }
203 
204       tableFormat(data)
205       console.log('Format tableData', data)
206       return data
207     },
208     /**
209      * 生成简单id 树形列表必须有id
210      */
211     generateId() {
212       return `id_${new Date().getTime()}${Math.floor(Math.random() * 10000)}`
213     },
214     // 数据类型改变
215     handleDataTypeChange(val, row) {
216       row.type = val
217       this.$set(row, 'type', val)
218       // 对象类型存在子节点
219       if (val === 'object') {
220         this.$set(row, 'child', [])
221       }
222     },
223     /**
224      * 生成一行数据
225      */
226     generateRow(parentId) {
227       return {
228         id: this.generateId(),
229         key: '',
230         type: '',
231         isEdit: true,
232         parentId
233       }
234     },
235     // 添加根节点
236     onAddRoot() {
237       this.tableData.push(this.generateRow('0'))
238     },
239     /**
240      * 处理添加一行数据
241      * @param {object} row 当前节点
242      * @param {number} index
243      * @param {string} type 操作类型 SIBLING 同级 / CHILD 子级
244      */
245     handleAddOneRow(row, index, type) {
246       const { parentId, id } = row
247       const curId = type === 'SIBLING' ? parentId : id
248       let curRow = {}
249       // 在 tableData 中,找到当前节点
250       const findRow = (data) => {
251         data.forEach((item) => {
252           if (item.id === curId) {
253             curRow = { ...item }
254           }
255           if (item.child && item.child.length) {
256             findRow(item.child)
257           }
258         })
259       }
260 
261       findRow(this.tableData)
262 
263       const { id: generateParentId, child } = curRow
264 
265       if (child) {
266         child.push(this.generateRow(generateParentId))
267       } else {
268         this.$set(curRow, 'child', [this.generateRow(generateParentId)])
269       }
270     },
271     // 添加同级节点
272     onAddSibling(row, index) {
273       console.log('onAddSibling', row, index)
274       const { parentId } = row
275       // 先判断是不是根节点
276       if (parentId === '0') {
277         // 当前节点直接添加
278         this.tableData.push(this.generateRow('0'))
279       } else {
280         this.handleAddOneRow(row, index, 'SIBLING')
281       }
282     },
283     // 添加子节点  todo
284     onAddChild(row, index) {
285       this.handleAddOneRow(row, index, 'CHILD')
286     },
287     // 编辑
288     onEdit(row) {
289       this.$set(row, 'isEdit', true)
290     },
291     // 保存
292     onSave(row) {
293       this.$set(row, 'isEdit', false)
294     },
295     // 删除当前节点及对应子节点数据
296     onDelete(row) {
297       const msg =
298         '<div><span style="color: #F56C6C">删除后将不可恢复</span>,你还要继续吗?</div>'
299       this.$confirm(msg, '提示', {
300         confirmButtonText: '确定',
301         cancelButtonText: '取消',
302         dangerouslyUseHTMLString: true,
303         type: 'warning'
304       })
305         .then(() => {
306           const { parentId, id } = row
307           // 根节点直接删除
308           if (parentId === '0') {
309             const delIndex = this.tableData.findIndex((item) => item.id === id)
310             this.tableData.splice(delIndex, 1)
311           } else {
312             // 找到父节点,通过父节点删除
313             let parentRow = {}
314             const findRow = (data) => {
315               data.forEach((item) => {
316                 if (item.id === parentId) {
317                   parentRow = { ...item }
318                 }
319                 if (item.child && item.child.length) {
320                   findRow(item.child)
321                 }
322               })
323             }
324             findRow(this.tableData)
325 
326             const { child } = parentRow
327 
328             const delIndex = child.findIndex((item) => item.id === id)
329 
330             child.splice(delIndex, 1)
331           }
332         })
333         .catch(() => {})
334     }
335   }
336 }
337 </script>
338 <style lang="scss" scoped>
339 .custom-tree-table {
340   height: 100%;
341   background-color: #fff;
342   padding: 20px;
343 }
344 </style>

 

标签:改等,el,tableData,节点,item,vue2,child,id,row
From: https://www.cnblogs.com/scallop/p/17695707.html

相关文章

  • 软件测试/人工智能|selenium元素定位方式大全
    前言当我们在使用selenium进行自动化测试工作时,元素定位是非常重要的一环,因为我们是借助脚本模拟我们通过鼠标和键盘对元素进行点击、输入内容和滑动操作的,所以准确的元素定位是我们执行测试脚本的重要一环。本文就来给大家介绍一下selenium的元素定位方式。find_element和find......
  • 软件测试/人工智能|一文教你配置selenium环境
    前言在软件开发过程中,自动化测试是确保应用程序质量的关键步骤之一。Python语言与Selenium库的结合为开发者提供了一个强大的工具,用于自动化Web应用程序的测试。本文将介绍如何配置Python和Selenium,搭建一个高效的自动化测试环境。Seleium安装我们可以直接在项目的虚拟环境中......
  • 2020CVPR_High-Resolution Image Synthesis with Latent Diffusion Models
    1.AutoEncoderAutoEncoder(自编码器)是一种无监督学习的神经网络模型,用于学习有效的数据表示。它的目标是将输入数据编码成一种潜在的、紧凑的表示形式,然后从这个表示中重构原始输入。自编码器由两部分组成:编码器(Encoder)和解码器(Decoder)。编码器(Encoder):将输入数据映射到潜在表示空......
  • 软件测试/人工智能|解决Selenium中的异常问题:“error sending request for url”
    前言在使用Selenium自动化测试时,有时会遇到“errorsendingrequestforurl”这样的异常。这个问题通常与Chrome浏览器驱动程序和网络请求相关。本文让我们来了解如何解决这个问题。问题原因这个异常通常出现在Selenium与Chrome浏览器交互时,可能由于网络请求或Chrome驱动程序......
  • Delphi Android程序启动过程
    文章转载于不得闲大师的文章,源文链 https://www.cnblogs.com/DxSoft/p/4460236.html Delphi的Android程序是原生的程序,也就是NativeActivity。那么就需要先看一下NativeActivity的原理,在AndroidManifest.xml文件里面指定入口activity为nativeactivity,这样应用程序一启动,jav......
  • js excel操作
    Js操作Excel常用方法Js操作Excel常用方法1.创建一个新Excel表格    varXLObj=newActiveXObject("Excel.Application");    varxlBook=XLObj.Workbooks.Add;                         //新增工作簿    varExcelSheet=xlBook.Wo......
  • Element Message相同文案重复提示处理
    import{Message}from'element-ui'letlastMessageText=nullletmessageTimer=nullexportdefaultfunctionshowMessage(options){ //如果新的消息与上一条消息相同,并且计时器还在运行,那么不显示新的消息 if(options.message===lastMessageText&&mess......
  • Linux 部署1Panel现代化运维管理面板教程并且实现远程访问(运维福音!!)
    1Panel是一个现代化、开源的Linux服务器运维管理面板。高效管理,通过Web端轻松管理Linux服务器,包括主机监控、文件管理、数据库管理、容器管理等下面我们介绍在Linux本地安装1Panel并结合cpolar内网穿透工具实现远程访问1Panel管理界面1.Linux安装1Panel执行如下......
  • Vue + Element UI 实现复制当前行数据功能(复制到新增页面组件值不能更新等问题解决)
    1、需求使用Vue+ElementUI实现在列表的操作栏新增一个复制按钮,复制当前行的数据可以打开新增弹窗后亦可以跳转到新增页面,本文实现为跳转到新增页面。2、实现1)列表页index.vue<el-table><!--其他列--><el-table-columnlabel="操作"width="150"><templateslot-scope=......
  • Java 类之 java.lang.reflect.Field
    Java类之java.lang.reflect.Field文章目录Java类之java.lang.reflect.Field一、概述1、java.lang.Class类获取字段的方法获取全部公有字段(含继承的,不含私有的)获取本类的所有字段(不含继承的,含私有的)代码示例2、java.lang.reflect.Field类简介3、类定义信息二、基本功能1、基......