基于vue+View UI的组织机构选择
1、效果
1、代码
<template><Button type="primary" @click="modal = true">点击选择</Button><div v-if="selectedArr.length > 0"><p>已选择项:</p><div v-for="(item, index) in selectedArr" :key="index">{{ item.title }}</div></div><Modal width="600" v-model="modal" title="请选择" @on-ok="ok" @on-cancel="cancel"><div class="tr-modal"><div class="org-tree"><div class="title"><span>组织机构</span><Input v-model="keyOrg" @on-change="onInput" placeholder="请输入"></Input></div><Treeref="tree"class="tree-main"@on-check-change="onCheckChange":data="data"show-checkboxcheck-directlymultiple></Tree></div><div class="to-right"><Icon type="ios-arrow-forward" size="30" color="#1890ff" /></div><div class="select-con"><div class="title"><span>已选择项</span></div><div class="to-right"><ul class="select-ul"><liv-for="(item, index) in selectedArr":key="index"@mouseenter="item.ishover = true"@mouseleave="item.ishover = false"@click="onDelete(index, item)"><span>{{ item.title }}</span><Icon v-if="item.ishover" type="md-close" size="20" color="#1890ff" /></li></ul></div></div></div></Modal>
</template>
<script>
export default {data() {return {modal: false,selectedArr: [],data: [{industryName: '农业、林业',node: 1,children: [{industryName: '农产品基地项目(含药材基地)',children: [],id: '01--001',title: '01--001_农产品基地项目(含药材基地)',industryState: '1',parentId: '01',industryCode: '01--001',},{industryName: '经济林基地项目',children: [],id: '01--002',title: '01--002_经济林基地项目',industryState: '1',parentId: '01',industryCode: '01--002',},],id: '01',title: '01_农业、林业',industryState: '1',parentId: null,industryCode: '01',},{industryName: '畜牧业',node: 1,children: [{industryName: '牲畜饲养;家禽饲养;其他畜牧业',children: [],id: '02--003',title: '02--003_牲畜饲养;家禽饲养;其他畜牧业',industryState: '1',parentId: '02',industryCode: '02--003',},],id: '02',title: '02_畜牧业',industryState: '1',parentId: null,industryCode: '02',},{industryName: '渔业',node: 1,children: [{industryName: '海水养殖',children: [],id: '03--004',title: '03--004_海水养殖',industryState: '1',parentId: '03',industryCode: '03--004',},{industryName: '内陆养殖',children: [],id: '03--005',title: '03--005_内陆养殖',industryState: '1',parentId: '03',industryCode: '03--005',},],id: '03',title: '03_渔业',industryState: '1',parentId: null,industryCode: '03',},],keyOrg: '',dataCp: [],}},methods: {ok() {this.$Message.info('Clicked ok')},cancel() {this.$Message.info('Clicked cancel')},onInput() {if (this.keyOrg) {let flattenTree = this.flattenTree(this.dataCp)let filterChildren = flattenTree.filter((el) => el.parentId && el.title.includes(this.keyOrg),)this.data = filterChildren} else {this.data = this.dataCp}},onDelete(index, item) {this.selectedArr.splice(index, 1)// 先扁平化let flattenTree = this.flattenTree(this.data)// 先根据parentId找到当前项的父节点let parent = flattenTree.find((el) => el.id == item.parentId)if (parent) {parent.checked = false}// 再取消勾选let checkItem = flattenTree.find((el) => el.id == item.id)checkItem.checked = false// 再还原成树形结构this.data = this.unflattenTree(flattenTree)},onCheckChange(prev, curr) {// 选中if (curr.checked) {// 选中的节点没有子节点(选中一个)if (curr.children.length === 0) {curr.ishover = falsethis.selectedArr.push(curr)} else {// 选中的节点有子节点(选中一个,然后勾选其子节点)curr.children.forEach((el) => {el.ishover = false})this.selectedArr.push(...curr.children)}} else {// 选中的节点没有子节点(选中一个)if (curr.children.length === 0) {// 取消let index = this.selectedArr.findIndex((item) => item.id == curr.id)// 删除已选项this.selectedArr.splice(index, 1)} else {curr.children.forEach((el) => {// 取消let index = this.selectedArr.findIndex((item) => item.id == el.id)// 删除已选项this.selectedArr.splice(index, 1)})}}},// 扁平化树形数据flattenTree(treeData) {const result = []function flatten(node) {// 创建新节点对象(浅拷贝,避免修改原对象)const newNode = { ...node }// 如果有子节点,先递归处理子节点if (Array.isArray(newNode.children) && newNode.children.length > 0) {// 临时保存子节点引用const children = newNode.children// 移除children属性(根据需求可选)delete newNode.children// 将当前节点加入结果result.push(newNode)// 递归处理子节点children.forEach((child) => flatten(child))} else {// 无子节点直接加入结果result.push(newNode)}}// 遍历所有根节点treeData.forEach((root) => flatten(root))return result},// 将扁平数据还原为树形结构unflattenTree(flatData) {// 创建ID映射字典和结果集const nodeMap = {}const roots = []// 第一遍遍历:创建所有节点的映射flatData.forEach((node) => {nodeMap[node.id] = { ...node, children: [] }})// 第二遍遍历:构建树形结构flatData.forEach((node) => {const currentNode = nodeMap[node.id]if (node.parentId) {// 找到父节点并添加到其childrenconst parent = nodeMap[node.parentId]if (parent) {parent.children.push(currentNode)}} else {// 根节点roots.push(currentNode)}})return roots},},mounted() {this.dataCp = JSON.parse(JSON.stringify(this.data))},
}
</script><style lang="scss" scoped>
.tr-modal {display: flex;height: 400px;.org-tree {width: 260px;height: 100%;border: 1px solid #d9d9d9;}.to-right {display: flex;align-items: center;margin: 0 5px;cursor: pointer;}.select-con {width: 260px;border: 1px solid #d9d9d9;}.title {height: 32px;display: flex;align-items: center;border-bottom: 1px solid #d9d9d9;span {display: flex;font-size: 14px;color: #1890ff;justify-content: center;align-items: center;width: 70px;font-weight: 700;}:deep(.ivu-input-wrapper) {flex: 1;width: auto;}}.tree-main {height: calc(100% - 32px);overflow-y: auto;}.select-ul {width: 100%;li {padding: 0 5px;height: 24px;display: flex;align-items: center;font-size: 14px;justify-content: space-between;margin: 4px 0;background-color: #eee;span {width: 90%;overflow: hidden;white-space: nowrap;}}}
}
</style>