Table of Contents
- 1. Function Description
-
- 1. Rendering
- 2. Description
- Two, realize
- 3. Import
1. Function description
1. Rendering image
2. Description
- Added: Add table parent row
- Added: Add table sub-rows
- delete: delete the parent row
- More dropdown menu (move up): Move parent row/child row up
- More Dropdown (Move Down): Move Parent/Child Rows Down
- EDIT: Only the name column is editable for parent rows, there is no limit for child rows. The trigger method is to click on the cell.
2. Implementation
<template>
<div class="box">
<span @click="addParent" style="margin-bottom:5px">
<a-icon type="plus-circle" style="margin-right:5px" /> New
</span>
<a-spin :spinning="spinning">
<vxe-table
border
resizable
auto-resize
show-overflow
highlight-hover-row
highlight-current-row
ref="xTable"
row-id="id"
max-height="500px"
:data="tableData"
:edit-config="{<!-- -->
trigger: 'click',
mode: 'cell',
activeMethod: activeMethod
}"
:tree-config="{ children: 'children' }"
>
<vxe-column field="name" title="name" :edit-render="{}" tree-node>
<template #edit="{ row }">
<vxe-input v-model="row.name" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column field="code" title="code" :edit-render="{}">
<template #edit="{ row }">
<vxe-input v-model="row.code" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column field="condition" title="condition" :edit-render="{}">
<template #edit="{ row }">
<vxe-input v-model="row.condition" type="text"></vxe-input>
</template>
</vxe-column>
<vxe-column title="Operation" fixed="right">
<template #default="{ row,rowIndex }">
<!-- parent row action button -->
<span v-if="'children' in row">
<span @click="addChild(row, rowIndex)" class="op-btn">Add</span>
<span @click="deleteParent(row, rowIndex)" class="op-btn">Delete</span>
<a-dropdown :trigger="['click']">
<a @click.prevent>
<a-icon type="dash" style="color:black;transform: rotate(90deg);" />
</a>
<template #overlay>
<a-menu>
<a-menu-item :key="0" @click="upRow(row, rowIndex)">Move up</a-menu-item>
<a-menu-item :key="1" @click="downRow(row, rowIndex)">Move down</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</span>
<!-- sub row operation button -->
<span v-else>
<span @click="deleteChild(row, rowIndex)" class="op-btn">delete</span>
<span @click="upRow(row, rowIndex)" class="op-btn">Move Up</span>
<span @click="downRow(row, rowIndex)" class="op-btn">Move Down</span>
</span>
</template>
</vxe-column>
</vxe-table>
</a-spin>
</div>
</template>
<script>
export default {<!-- -->
data() {<!-- -->
return {<!-- -->
spinning: false, // table loading status
tableData: [], // table data
moveArray: [] // Move the array
}
},
async created() {<!-- -->
await this. getTableData()
},
methods: {<!-- -->
// get table data
async getTableData() {<!-- -->
// 1. Turn on the form loading state
this.spinning = true
// 2. Get form data
this.tableData = [
{<!-- -->
creator: 'Xiaohong',
code: 'xh',
name: 'Craft 1',
description: '',
condition: '',
id: 1,
children: [
{<!-- -->
creator: '红红',
code: 'hh',
name: 'Craft 11',
condition: '',
description: '',
id: 11
},
{<!-- -->
creator: 'Flower',
code: 'hh',
name: 'Craft 12',
condition: '',
description: '',
id: 12
},
{<!-- -->
creator: 'aa',
code: 'hh',
name: 'Craft 13',
condition: '',
description: '',
id: 13
}
]
},
{<!-- -->
creator: 'Xiaolan',
code: 'xl',
name: 'Craft 2',
description: '',
condition: '',
id: 2,
children: [
{<!-- -->
creator: 'Blue',
code: 'll',
name: 'Craft 22',
condition: '',
description: '',
id: 21
}
]
},
{<!-- -->
creator: 'Xiaobai',
code: 'xb',
name: 'Craft 3',
condition: '',
description: '',
id: 3,
children: []
}
]
// 3. Close the form loading state
this. spinning = false
},
// Add table parent row
addParent() {<!-- -->
const record = {<!-- -->
name: '',
code: '',
condition: '',
id: this. getGUID()
}
this. tableData. push(record)
this.$message.success('Add successfully!')
},
// delete parent row
deleteParent(row, rowIndex) {<!-- -->
this.tableData.splice(rowIndex, 1)
this.$message.success('delete successfully!')
},
// delete child
deleteChild(row) {<!-- -->
let info = this. findChildIndex(this. tableData, row. id)
this.tableData[info[1]].children.splice(info[0], 1)
this.$message.success('delete successfully!')
},
// move down
downRow(row, rowIndex) {<!-- -->
// 1. The default is the data of the parent row
let index = rowIndex
this.moveArray = this.tableData
// 2. If you move up the child row, you need to find the child row index and the parent row index
if ('children' in row === false) {<!-- -->
// Find child row, parent row index
let info = this. findChildIndex(this. tableData, row. id)
// update data
index = info[0]
this.moveArray = this.tableData[info[1]].children
}
// 3. is the last line
if (index === this.moveArray.length - 1) {<!-- -->
this.$message.warning('This line cannot be moved down!')
} else {<!-- -->
// 4. Not the last line
this.moveArray = this.swapItem(this.moveArray, index, index + 1)
this.$message.success('Move down successfully!')
}
},
// move up
upRow(row, rowIndex) {<!-- -->
// 1. The default is the data of the parent row
let index = rowIndex
this.moveArray = this.tableData
// 2. If you move up the child row, you need to find the child row index and the parent row index
if ('children' in row === false) {<!-- -->
// Find child row, parent row index
let info = this. findChildIndex(this. tableData, row. id)
// update data
index = info[0]
this.moveArray = this.tableData[info[1]].children
}
// 3. is the first row
if (index === 0) {<!-- -->
this.$message.warning('This line cannot be moved up!')
} else {<!-- -->
// 4. Not the first line
this.moveArray = this.swapItem(this.moveArray, index, index - 1)
this.$message.success('Move up successfully!')
}
},
swapItem(arr, index1, index2) {<!-- -->
arr[index1] = arr.splice(index2, 1, arr[index1])[0]
return arr
},
// Check if the cell is editable
activeMethod({<!-- --> row, column }) {<!-- -->
// parent row only name editable
let tag =
'children' in row & amp; & amp;
['code', 'condition'].includes(column.property)
return !tag
},
// add child
addChild(row, rowIndex) {<!-- -->
const record = {<!-- -->
name: '',
code: '',
condition: '',
description: '',
id: this. getGUID()
}
this.tableData[rowIndex].children.push(record)
this.$message.success('Add successfully!')
},
// Find the index of the child row, the index of the parent row
findChildIndex(data, id) {<!-- -->
let result = []
function find(data, id, parentId) {<!-- -->
for (let i = 0; i < data. length; i ++ ) {<!-- -->
if (result. length > 0) {<!-- -->
return
}
if (data[i].id === id) {<!-- -->
result. push(i)
result. push(parentId)
}
if (data[i].children & amp; & amp; data[i].children.length > 0) {<!-- -->
find(data[i].children, id, i)
}
}
}
find(data, id, 0)
return result
},
// randomly generate id
getGUID() {<!-- -->
var d = new Date().getTime()
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
function (c) {<!-- -->
var r = (d + Math.random() * 16) % 16 | 0
d = Math. floor(d / 16)
return (c == 'x' ? r : (r & amp; 0x3) | 0x8).toString(16)
}
)
return uuid
}
}
}
</script>
<style>
.box {<!-- -->
width: 1000px;
margin: 100px;
}
.op-btn {<!-- -->
margin: 0 2px;
cursor: pointer;
}
</style>
3. Import
- This feature uses Ant Design + vxe-table
- For related import instructions, please refer to the article