基於
antd vue 1.7.x版本的樹結構,可自行增刪改子集。原創文章。
效果圖:

源碼:
<template>
<div>
<p :class="{title, required: isRequired}">{{ title }}</p>
<div class="content">
<a-tree
show-line
:tree-data="treeSource"
:expandedKeys="expandedData"
@expand="handleExpand"
>
<a-icon slot="icon" type="carry-out" />
<!-- 插槽名稱寫死為custom,需要傳入的treeData中需要用到自定義插槽的對象中增加scopedSlots: { title: "custom" }屬性 -->
<template slot="custom" slot-scope="item">
<span v-if="item.isEdit">
<a-input ref="new" type="text" class="editInput" v-model.trim="newItem" @pressEnter="addConfirm(item)" />
<a-icon type="close-circle" @click="addCancel(item)"/>
<a-icon type="check-circle" @click="addConfirm(item)"/>
</span>
<div v-else>
<span class="node-title">{{ item.title }}</span>
<span class="icon-wrap">
<a-icon type="plus" title="添加子集" @click="addItem(item)" />
</span>
<span class="icon-wrap">
<a-icon type="edit" title="編輯" @click="editItem(item)" />
</span>
<span class="icon-wrap">
<a-icon type="delete" title="刪除" @click="deleteItem(item)" />
</span>
</div>
</template>
</a-tree>
<a-button type="primary" @click="addFirstItem">增加一級節點</a-button>
</div>
</div>
</template>
<script>
export default {
name: 'treeEdit',
props: {
isRequired: {
type: Boolean,
default: true
},
title: {
type: String,
default: ''
},
// 樹的數據源,結構{scopedSlots: { title: "custom" },//插槽名稱寫死為custom,children:[],key:xx,title:xx,level:xx,isEdit:true}
treeData: {
type: Array,
default: () => {
return []
}
},
defaultExpanded: {
type: Array,
default: () => {
return []
}
}
},
data () {
return {
treeSource: [],
expandedData: [],
newItem: '',
editFlag: false, // 記錄是否有已經處於編輯狀態的標識
newFlag: false, // 記錄是否是新建節點,用於在cancel的時候判斷是該刪除還是恢復原樣
}
},
methods: {
handleExpand (expandedKeys) {
this.expandedData = expandedKeys
},
addFirstItem () {
if (this.editFlag) {
this.$message.warning('已有編輯項,請先編輯完成!')
return
}
this.treeSource.push({
children: [],
key: this.getItemNextId(),
title: '',
level: 1,
scopedSlots: { title: "custom" },
isEdit: true
})
this.newItem = ''
this.editFlag = true
this.newFlag = true
this.$nextTick(() => {
this.$refs.new.focus()
})
},
addItem (item) {
if (this.editFlag) {
this.$message.warning('已有編輯項,請先編輯完成!')
return
}
item.children.push({
children: [],
key: this.getItemNextId(),
title: '',
level: item.level + 1,
scopedSlots: { title: "custom" },
isEdit: true
})
this.newItem = ''
this.editFlag = true
this.newFlag = true
this.expandedData.push(item.key)
this.$nextTick(() => {
this.$refs.new.focus()
})
},
editItem (item) {
if (this.editFlag) {
this.$message.warning('已有編輯項,請先編輯完成!')
return
}
item.isEdit = true
this.newFlag = false
this.editFlag = true
this.newItem = item.title
this.treeListItemActions(this.treeSource, item.key, 'edit', item)
this.$nextTick(() => {
this.$refs.new.focus()
})
},
deleteItem (item) {
if (item.children.length > 0) {
let that = this
this.$confirm({
title: '操作提示',
content: '將會刪除此節點的所有子節點,確定刪除?',
centered: true,
onOk () {
that.treeListItemActions(that.treeSource, item.key, 'delete')
},
onCancel () {
that.$message.info('已取消')
}
})
} else {
this.treeListItemActions(this.treeSource, item.key, 'delete')
}
},
addCancel (item) {
this.newItem = ''
item.isEdit = false
this.editFlag = false
this.treeListItemActions(this.treeSource, item.key, this.newFlag ? 'delete' : 'edit', item)
},
addConfirm (item) {
if (this.newItem !== null && this.newItem.trim() !== '') {
item.title = this.newItem
item.isEdit = false
this.editFlag = false
this.treeListItemActions(this.treeSource, item.key, 'edit', item)
} else {
this.$message.warning('不允許為空')
}
},
// 獲取新節點的id(時間戳加隨機數)
getItemNextId () {
return (new Date().getTime() + Math.ceil(Math.random() * 10000)).toString()
},
// 根據id屬性從數組(樹結構)中匹配元素,執行action操作
treeListItemActions(treeList, key, action, item) {
if (!treeList || !treeList.length) {
return
}
for (let i = 0; i < treeList.length; i++) {
if (treeList[i].key === key) {
if (action === 'delete') {
treeList.splice(i, 1)
this.$forceUpdate()
} else if (action === 'edit') {
treeList[i] = item
this.$forceUpdate()
}
break
}
this.treeListItemActions(treeList[i].children, key, action, item)
}
},
// 提供給調用方獲取樹的數據源的方法
getData () {
return this.treeSource
}
},
created () {
this.treeSource = JSON.parse(JSON.stringify(this.treeData))
this.expandedData = JSON.parse(JSON.stringify(this.defaultExpanded))
}
}
</script>
<style lang="less" scoped>
.title {
color: rgba(0, 0, 0, 0.85);
}
.required {
&::before {
display: inline-block;
margin-right: 4px;
color: #f5222d;
font-size: 14px;
font-family: SimSun, sans-serif;
line-height: 1;
content: '*';
}
}
.content {
margin-left: 20px;
.icon-wrap {
padding: 0 8px;
}
.editInput {
padding: 0;
margin-right: 5px;
height: 24px;
}
}
</style>