diaLogTree
<template>
<div>
<el-tree @node-click="handleNodeClick" :data="props.fileTree" :props="defaultProps" node-key="id"
:current-node-key="currentNodeKey" :default-expanded-keys="currentNodeKeyList" :highlight-current="true"
:expand-on-click-node="false" :default-expand-all="true">
<template #default="{ node, data }">
<span class="custom-tree-node fix justify-content-content align-items-content">
<span @click.stop="onChange(data)">{{ node.label }}</span>
<el-dropdown>
<span class="el-dropdown-link" v-if="isEdit">
<el-icon class="el-icon--right">
<Setting />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="onNode('add', data)">添加</el-dropdown-item>
<el-dropdown-item @click="onNode('edit', data)" :disabled="node.level === 1">编辑</el-dropdown-item>
<el-dropdown-item @click="onNode('rem', data)" :disabled="node.level === 1">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</span>
</template>
</el-tree>
<el-dialog v-model="dialogVisible" :title="dialogVisibletitle" width="400">
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="100px" class="demo-ruleForm"
:size="formSize" status-icon>
<el-form-item label="树节点名称" prop="dptName">
<el-input v-model.trim="ruleForm.dptName" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm(ruleFormRef)">
确定
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, reactive, nextTick } from 'vue';
import { Setting } from '@element-plus/icons-vue'
import type { FormInstance, FormRules } from 'element-plus'
import { RuleFormFileTree } from './type'
const props = defineProps({
fileTree: {
type: Array,
default: () => []
},
currentNodeKey: {
type: String,
default: ''
},
currentNodeKeyList: {
type: Array,
default: () => []
},
isEdit: {
type: Boolean,
default: true
},
treeName: {
type: String,
default: 'label'
}
})
const emits = defineEmits(['changeTree', 'deleteTree', 'addTree', 'editTree', 'changeTreeNode'])
/**
* @description: 弹窗数据
* @return {*}
*/
const dialogVisible = ref(false)
const ruleFormRef = ref()
const diaTitle = ref('add')
const dialogVisibletitle = computed(() => {
return diaTitle.value === 'add' ? '添加' : diaTitle.value === 'edit' ? '编辑' : ''
})
const formSize = ref('default')
const ruleForm = reactive<RuleFormFileTree>({
dptName: ''
})
const rules = reactive<FormRules<RuleFormFileTree>>({
dptName: [
{ required: true, message: '不能为空', trigger: 'blur' },
],
})
/**
* @description: 组织树格式
* @return {*}
*/
const defaultProps = {
children: 'children',
label: props.treeName,
}
/**
* @description: 接口穿参数据
* @return {*}
*/
const dataParams: any = reactive({
id: null,
level: null,
dptName: null,
pid: null
})
const onChange = (v: any) => {
emits('changeTree', v.id)
}
const handleNodeClick = (v: any) => {
emits('changeTreeNode', v.id)
}
/**
* @description: 点击弹窗并获取对应的数据
* @param {*} ty
* @param {*} data
* @return {*}
*/
const onNode = async (ty: string, data: any) => {
dataParams.pid = data.id,
dataParams.id = data.id,
dataParams.level = data.level,
ruleForm.dptName = ''
diaTitle.value = ty
if (ty === 'edit') {
ruleForm.dptName = data[props.treeName]
dialogVisible.value = true
} else if (ty === 'add') {
dialogVisible.value = true
}
if (ty === 'rem') {
emits('deleteTree', data.id)
}
}
/**
* @description: 点击保存
* @param {*} formEl
* @return {*}
*/
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
if (diaTitle.value === 'add') {
let params = {
...dataParams
}
params.level = params.level++
params.dptName = ruleForm.dptName
emits('addTree', params)
dialogVisible.value = false
} else {
let params = {
...dataParams
}
params.dptName = ruleForm.dptName
delete params.pid
emits('editTree', params)
dialogVisible.value = false
}
}
})
}
</script>
<style lang="scss" scoped>
::v-deep .el-tree-node.is-current>.el-tree-node__content {
color: $theme_colors;
}
</style>
baseTree
<template>
<el-tree :data="dataSource" node-key="id" default-expand-all :expand-on-click-node="false">
<template #default="{ node, data }">
<span class="custom-tree-node">
<el-input v-if="data.id === dataDepartmentTree.flagId && dataDepartmentTree.isView" v-model.trim="data.label"
style="width: 120px;margin-left: 8px;"></el-input>
<span v-else>{{ node.label }}</span>
<span>
<a @click="edit(data, node)" v-show="!data.editAble && node.level !== 1">编辑</a>
<a @click="handleSuccess(data)" style="margin-left: 8px" v-show="data.editAble && node.level !== 1">完成</a>
<a @click="append(data, node)" style="margin-left: 8px"> 新增 </a>
<a style="margin-left: 8px" @click="remove(node, data)" v-if="node.level !== 1"> 删除 </a>
</span>
</span>
</template>
</el-tree>
</template>
<script lang="ts" setup>
// import { createTree, deleteTree, updateTree } from '@/api/systeManagement/userManagement'
import { reactive, ref, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { PropsTree, Tree, DataDepartmentTree } from './type'
import type Node from 'element-plus/es/components/tree/src/model/node'
/**
* 部门树的data响应式数据
*/
const dataDepartmentTree = reactive<DataDepartmentTree>({
flagId: 0,
isView: false,
})
const dataSource = ref<Tree[]>([])
/**
* 父组件逻辑
*/
//通过emit向父组件传参
const emit = defineEmits([
'ok',
'close',
'create',
'remove',
"update"
])
//父组件传参
const props: PropsTree = defineProps({
dataTree: {
type: Array,
default: () => []
}
})
watch(() => props.dataTree, (newData, oldData) => {
console.log('old', oldData);
dataSource.value = [...newData]
}, { deep: true, immediate: true })
/**
* @description: 新增部门
* @param {*} data
* @return {*}
*/
const append = async (data: Tree, node: any) => {
console.log('node', node, data);
let date = new Date().getTime()
let dataParams = {
id: null,
level: data.level++,
dptName: `新增${String(date).slice(7, String(date).length)}`,
pid: data.id
}
emit('create', dataParams)
}
/**
* @description: 删除部门
* @param {*} node
* @param {*} data
* @return {*}
*/
const remove = (node: any, data: Tree) => {
console.log("node", node);
emit('remove', data.id)
}
/**
* @description: 编辑部门名称
* @param {*} data
* @param {*} node
* @return {*}
*/
const edit = (data: Tree, node: Node) => {
dataDepartmentTree.flagId = data.id
dataDepartmentTree.isView = true
const parent = node.parent
const children: Tree[] = parent.data.children || parent.data
recursionData(dataSource.value, data.id, true)
}
const recursionData = (arr: any, id: any, flagView: boolean) => {
return arr.map((el: { id: any; editAble: boolean; children: any; }) => {
if (el.id && el.id === id) {
el.editAble = flagView
} else {
recursionData(el.children, id, flagView)
}
});
}
/**
* @description: 点击完成
* @param {*} node
* @param {*} data
* @return {*}
*/
const handleSuccess = (data: Tree) => {
if (!data.label) {
ElMessage.error({
message: '部门名称不能为空',
type: 'error',
})
} else {
if (data.label.length < 11) {
dataDepartmentTree.isView = false
dataDepartmentTree.isView = false
recursionData(dataSource.value, data.id, false)
dataSource.value = [...dataSource.value]
let dataParams = {
id: data.id,
dptName: data.label,
level: data.level,
}
emit("update", dataParams)
} else {
ElMessage.error({
message: '部门名称不能超出10个字符',
type: 'error',
})
}
}
}
</script>
<style lang="less" scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.el-tree {
--el-tree-node-content-height: 35px;
}
</style>