<template>
<page-header-wrapper>
<a-card class="container">
<div class="tree-container">
<div class="left">
<div class="left-input">
<a-input placeholder="请输入" style="width: 200px;margin-right: 10px"/>
<a-button @click="newlyAdded" class="add">新增</a-button>
</div>
<!-- 树形组织架构树 -->
<Tree :tree-data="treeData" :defaultExpandAll="false" draggable @dragenter="onDragEnter" @drop="onDrop">
<!-- 每个父级前的图标 -->
<span slot="switcherIcon" class="icon-plus"></span>
<template slot="custom" slot-scope="item">
<div>
<!-- 名称 -->
<span class="node-title">{{ item.title }} </span>
<span style="margin-left: 20px">
<!-- 新增 -->
<span style="margin-right: 10px;">
<a-icon type="plus-circle" @click="subordinateItem(item)"/>
</span>
<!-- 编辑 -->
<span style="margin-right: 10px;">
<a-icon type="edit" @click="modifyItem(item)"/>
</span>
<!-- 删除 -->
<span style="margin-right: 10px;">
<a-popconfirm title="是否要删除此行?" @confirm="deleteItem(item)">
<a-icon type="delete"/>
</a-popconfirm>
</span>
</span>
</div>
</template>
</Tree>
</div>
<div class="right">
<div class="right-btn">
<a-button type="primary"><a-icon type="plus" />添加</a-button>
</div>
<!-- 表格开始 -->
<div class="container-table">
<a-table
:columns="columns"
:data-source="data"
>
<span slot="action" slot-scope="text, record">
<template>
<a @click="handleEnable(record)">启用</a>
</template>
<a-divider type="vertical" />
</span>
</a-table>
</div>
<!-- 表格结束 -->
</div>
</div>
</a-card>
<!-- 弹窗开始 -->
<tree-lower-modules ref="treeLowerModules" @ok="onOk"/>
<!-- 弹窗结束 -->
</page-header-wrapper>
</template>
<script>
import treeLowerModules from './components/treeLowerModules.vue'
import { Tree } from 'ant-design-vue'
import { projectList } from '@/views/authority/project/table-header'
export default {
name: 'OrganizateTree',
components: { treeLowerModules, Tree },
props: {},
data () {
return {
// 组织树数据
treeData: [],
selectKeys: [],
// 表格数据
data: [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
state: 1
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
state: 1
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
state: 1
}
],
// 表格行
columns: projectList
}
},
filters: {},
computed: {},
watch: {},
created () {
this.getTreeData()
},
mounted () {
},
beforeDestroy () {
},
methods: {
/**
* 组织树的数据
*/
getTreeData () {
this.treeData = [
{
title: '0',
key: '0',
scopedSlots: { title: 'custom' },
children: [
{
title: '0-0',
key: '0-0',
scopedSlots: { title: 'custom' },
children: [
{
title: '0-0-0',
key: '0-0-0',
scopedSlots: { title: 'custom' }
},
{
title: '0-0-1',
key: '0-0-1',
scopedSlots: { title: 'custom' }
},
{
title: '0-0-2',
key: '0-0-2',
scopedSlots: { title: 'custom' }
}
]
},
{
title: '0-1',
key: '0-1',
scopedSlots: { title: 'custom' },
children: [
{
title: '0-1-0',
key: '0-1-0',
scopedSlots: { title: 'custom' }
},
{
title: '0-1-1',
key: '0-1-1',
scopedSlots: { title: 'custom' }
},
{
title: '0-1-2',
key: '0-1-2',
scopedSlots: { title: 'custom' }
}
]
},
{
title: '0-2',
key: '0-2',
scopedSlots: { title: 'custom' }
}
]
},
{
title: '1',
key: '1',
scopedSlots: { title: 'custom' },
children: [
{
title: '1-1',
key: '1-1',
scopedSlots: { title: 'custom' }
},
{
title: '1-2',
key: '1-2',
scopedSlots: { title: 'custom' }
},
{
title: '1-3',
key: '1-3',
scopedSlots: { title: 'custom' }
}
]
},
{
title: '2',
key: '2',
scopedSlots: { title: 'custom' }
}
]
// 请求接口
// getTreeList().then(res => {
// console.log(res.result)
// const result = res.result
// 不一样字段的时候替换下
// this.treeData = JSON.parse(JSON.stringify(result).replace(/"parent_id"/g,
// '"key"'))
// this.treeData = JSON.parse(JSON.stringify(result).replace(/"group_name"/g,
// '"title"'))
// 每一项都加 scopedSlots: { title: 'custom' }
// this.handleData(this.treeData)
// https://www.cnblogs.com/xianglian/p/15469956.html
// https://blog.csdn.net/qq_41579104/article/details/115617070
// })
},
// 递归每一项都加 scopedSlots: { title: 'custom' }
// handleData (tree) {
// for (const item of tree) {
// item['scopedSlots'] = { title: 'custom' }
// if (item.children && item.children.length) {
// this.handleData(item.children)
// }
// }
// },
/**
* 组织树新增按钮
*/
newlyAdded () {
const item = { key: this.treeData[0].key, operation: 1 }
this.$refs.treeLowerModules.add(item)
},
/**
* 添加下級
* @param item
*/
subordinateItem (item) {
item.operation = 2
this.$refs.treeLowerModules.add(item)
},
/**
* 修改
* @param item
*/
modifyItem (item) {
this.$refs.treeLowerModules.edit(item)
},
/**
* 刪除
* @param item
*/
deleteItem (item) {
this.selectKeys = [item.key]
this.dataDriveDelete()
},
/**
* 確定按鈕
* @param val
*/
onOk (val) {
// 1:一级新增, 3:编辑, 2:二级新增
if (val.operation === 1) {
this.selectKeys = [val.key]
this.dataDriveAddSame(val.title)
} else if (val.operation === 2) {
this.selectKeys = [val.key]
this.dataDriveAddSub(val.title)
} else if (val.operation === 3) {
this.selectKeys = [val.key]
this.dataDriveModify(val.title)
}
},
/**
* 公共父级修改方法
* @param childs: 组织树数据
* @param findKey 目标key
*/
getTreeDataByKey (childs = [], findKey) {
let finditem = null
for (let i = 0, len = childs.length; i < len; i++) {
const item = childs[i]
if (item.key !== findKey && item.children && item.children.length > 0) {
finditem = this.getTreeDataByKey(item.children, findKey)
}
if (item.key === findKey) {
finditem = item
}
if (finditem != null) {
break
}
}
return finditem
},
/**
* 公共父级方法
* @param childs: 组织树数据
* @param findKey 目标key
*/
getTreeParentChilds (childs = [], findKey) {
let parentChilds = []
for (let i = 0, len = childs.length; i < len; i++) {
const item = childs[i]
if (item.key !== findKey && item.children && item.children.length > 0) {
parentChilds = this.getTreeParentChilds(item.children, findKey)
}
if (item.key === findKey) {
parentChilds = childs
}
if (parentChilds.length > 0) {
break
}
}
return parentChilds
},
/**
* 添加同级
* @param title
*/
dataDriveAddSame (title) {
const parentChilds = this.getTreeParentChilds(
this.treeData,
this.selectKeys[0]
)
// 校验 相同的不能不可以添加
const existence = parentChilds.find(item => { return item.key === title })
if (!existence) {
parentChilds.push({
title: title,
key: new Date().getTime(),
scopedSlots: { title: 'custom' }
})
} else {
this.$message.success('此数据已存在')
return false
}
},
/**
* 添加下级
* @param title
*/
dataDriveAddSub (title) {
const selectItem = this.getTreeDataByKey(this.treeData, this.selectKeys[0])
if (!selectItem.children) {
this.$set(selectItem, 'children', [])
}
// 校验 相同的不能不可以添加
const existence = selectItem.children.find(item => { return item.title === title })
if (!existence) {
selectItem.children.push({
title: title,
key: new Date().getTime(),
scopedSlots: { title: 'custom' }
})
} else {
this.$message.success('此数据已存在')
return false
}
this.$forceUpdate()
},
/**
* 一级修改
* @param title
*/
dataDriveModify (title) {
const selectItem = this.getTreeDataByKey(this.treeData, this.selectKeys[0])
selectItem.title = title
},
/**
* 删除方法
*/
dataDriveDelete () {
const parentChilds = this.getTreeParentChilds(
this.treeData,
this.selectKeys[0]
)
// 对删除的数据若下面有子级就给与提示不允许删除
parentChilds.map(item => {
console.log(item.children, '000')
return item.children
})
// console.log(noeDel, 'shanchu')
const delIndex = parentChilds.findIndex(
(item) => item.key === this.selectKeys[0]
)
parentChilds.splice(delIndex, 1)
},
/**
* 拖拽
* @param info
*/
onDragEnter (info) {
console.log(info, '12222')
// expandedKeys 需要受控时设置
// this.expandedKeys = info.expandedKeys
},
/**
* 拖拽
* @param info
*/
onDrop (info) {
console.log(info)
const dropKey = info.node.eventKey
const dragKey = info.dragNode.eventKey
const dropPos = info.node.pos.split('-')
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1])
const loop = (data, key, callback) => {
data.forEach((item, index, arr) => {
if (item.key === key) {
return callback(item, index, arr)
}
if (item.children) {
return loop(item.children, key, callback)
}
})
}
const data = [...this.treeData]
// Find dragObject
let dragObj
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1)
dragObj = item
})
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, item => {
item.children = item.children || []
// where to insert 示例添加到尾部,可以是随意位置
item.children.push(dragObj)
})
} else if (
(info.node.children || []).length > 0 && // Has children
info.node.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
loop(data, dropKey, item => {
item.children = item.children || []
// where to insert 示例添加到尾部,可以是随意位置
item.children.unshift(dragObj)
})
} else {
let ar
let i
loop(data, dropKey, (item, index, arr) => {
ar = arr
i = index
})
if (dropPosition === -1) {
ar.splice(i, 0, dragObj)
} else {
ar.splice(i + 1, 0, dragObj)
}
}
this.treeData = data
},
handleEnable () {
}
}
}
</script>
<style scoped lang="less">
::v-deep .ant-tree-switcher.ant-tree-switcher_open {
.icon-plus {
background-image: url("../../../assets/icons/minus.png"); // 展开节点时的icon
background-size: 24px;
width: 24px;
height: 24px;
}
}
::v-deep .ant-tree-switcher.ant-tree-switcher_close {
.icon-plus {
background-image: url("../../../assets/icons/add.png"); // 收起节点时的icon
background-size: 24px;
width: 24px;
height: 24px;
}
}
.icon-wrap {
margin: 0 6px;
}
.node-title {
padding-right: 15px;
}
.tree-cancle_icon {
margin: 0 6px;
}
.org-input {
display: flex;
flex-direction: row;
}
.add {
background: #1890ff;
color: #FFFFFF;
}
/deep/ .ant-card-body {
padding: 50px 60px;
}
.tree-container {
display: flex;
flex-direction: row;
.left-input{
display: flex;
flex-direction: row;
margin-bottom: 15px;
}
.right{
margin-left: 50px;
width: 100%;
&-btn{
float: right;
margin-bottom: 20px;
}
}
}
</style>
操作弹窗:treeModules.vue
<template>
<a-modal :title="title" v-model="visible" :confirmLoading="confirmLoading" @ok="handleSubmit">
<a-form :form="form">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="名称">
<a-input :disabled="disabled" placeholder="请输入名称" v-decorator="['title', {rules: [{ required: true, message: '请输入名称' }]}]" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script>
import pick from 'lodash.pick'
export default {
address: 'TreeModules',
props: {},
components: {},
data () {
return {
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 5 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
title: '',
mdl: {},
disabled: false,
operation: 1,
record: {}
}
},
beforeCreate () {},
created () {},
computed: {},
methods: {
/**
* 新增彈窗
* @param record
*/
add (record) {
this.operation = record.operation
if (record.operation === 1) {
this.title = '新增'
} else {
this.title = '添加下级'
}
this.form.resetFields()
this.visible = true
this.disabled = false
this.record = record
},
/**
* 編輯彈窗
* @param record
*/
edit (record) {
this.record = record
this.operation = 3
this.disabled = false
this.title = '编辑名称'
this.mdl = Object.assign({}, record)
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.mdl,
'title'
))
})
},
/**
* 保存
* @param e
*/
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
values.operation = this.operation
values.key = this.record.key
this.confirmLoading = true
this.$emit('ok', values)
this.form.resetFields()
this.$message.success('保存成功')
this.visible = false
this.confirmLoading = false
}
})
}
}
}
</script>
<style scoped lang="less">
</style>