<template>
<div>
<a-form-model-item label="参与人" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-button type="primary" icon="plus" @click="onClickParticipatePerson">选择人员</a-button>
<div class="person_container">
<div class="person_item" :key="index" v-for="(item, index) in personCheckList">
{{ item.dataRef.title }}
<a-button
icon="close"
size="small"
class="selected_del"
@click="clearParticipatePersonItem(item.dataRef.key, index, item)"
/>
</div>
</div>
</a-form-model-item>
<a-modal
width="666px"
:footer="null"
title="选择成员"
@cancel="handleCancel"
:visible="personVisible"
:confirm-loading="confirmLoading"
>
<div class="flex">
<a-card style="width: 50%; border: none">
<a-spin :spinning="loading">
<div class="treeNode">
<a-input-search v-model="searchText" style="margin-bottom: 8px" placeholder="输入部门或成员名称" />
<a-tree
key="id"
checkable
show-icon
@check="onCheck"
@expand="onExpand"
:checkStrictly="false"
:defaultExpandAll="true"
:tree-data="filteredData"
:checkedKeys="checkedKeys"
:expandedKeys.sync="expandedKeys"
:auto-expand-parent="autoExpandParent"
>
<template slot="title" slot-scope="text">
<span v-if="text.title.indexOf(searchText) > -1">
{{ text.title.substr(0, text.title.indexOf(searchText)) }}
<span style="color: #ff0000">{{ searchText }}</span>
{{ text.title.substr(text.title.indexOf(searchText) + searchText.length) }}
</span>
<span v-else>{{ text.title }}</span>
</template>
</a-tree>
</div>
</a-spin>
</a-card>
<a-card style="width: 50%" :title="`已选:${searchData.length}`" size="small">
<a slot="extra" @click="clearAll">清空</a>
<div class="selected_list treeNode">
<div class="selected_item" v-for="(item, index) in searchData" :key="index">
{{ item.dataRef.title }}
<a-button
icon="close"
size="small"
class="selected_del"
@click="clearItem(item.dataRef.key, index, item)"
/>
</div>
</div>
</a-card>
</div>
<div class="group-footer">
<a-button style="margin-right: 10px" @click="handleCancel">取消</a-button>
<a-button type="primary" @click="handleOver">确定</a-button>
</div>
</a-modal>
</div>
</template>
<script>
export default {
components: {},
props: {
// 树状图原始数据
personList: {
type: Array,
default: () => [],
},
// 树状图选中数据
personCheckList: {
type: Array,
default: () => [],
},
// 弹窗显示
personVisible: {
type: Boolean,
default: false,
},
},
watch: {
personVisible: {
handler() {
this.initData()
},
},
},
data() {
return {
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
// 树状图数据
treeData: [],
// 加载状态
loading: false,
// 输入框
searchText: '',
// 选中数据
searchData: [],
checkedKeys: [],
expandedKeys: [],
// 默认选中数据
// defaultCheckList: [],
confirmLoading: false,
// 父节点自动展开
autoExpandParent: true,
}
},
computed: {
// 根据搜索关键字过滤数据
filteredData() {
return this.searchText ? this.filterData(this.treeData, this.searchText) : this.treeData
},
},
created() {},
methods: {
// 初始化数据
initData() {
// 将默认选中数据中的key值存储checkedKeys数组中
if (this.personCheckList.length > 0) {
this.searchData = this.personCheckList
this.checkedKeys = this.personCheckList.map((item) => item.dataRef.key)
}
this.treeData = this.processData(this.personList)
},
onClickParticipatePerson(e) {
this.personVisible = true
},
// 删除当前选中审批人员数据
clearParticipatePersonItem(id, index, record) {
this.personCheckList.splice(index, 1)
},
// 树状图数据处理
processData(data) {
let result = []
data.forEach((item) => {
// 生成树状数据节点
let nodeItem = {
key: item.id,
children: [],
title: item.name,
}
// 人员列表
if (item.personList && item.personList.length > 0) {
nodeItem.children = this.processPersonList(item.personList)
}
// 如果当前节点包含下级子节点 递归调用方法
if (item.children && item.children.length > 0) {
nodeItem.children.push(...this.processData(item.children))
}
result.push(nodeItem)
})
return result
},
// 人员数据处理
processPersonList(personList) {
return personList.map((item) => ({
key: item.id,
title: item.realname,
}))
},
// 输入框搜索
filterData(data, searchText) {
const result = []
for (let item of data) {
if (item.title.includes(searchText)) {
result.push(item)
continue
}
if (item.children) {
const children = this.filterData(item.children, searchText)
if (children.length > 0) {
this.expandedKeys.push(item.key)
result.push({ ...item, children })
}
}
}
return result
},
// 人员选择完成
handleOver() {
this.confirmLoading = true
// 将选中数据传递向父组件
this.$emit('personCheckUpdate', this.searchData)
// 关闭弹窗
this.$emit('personCancel', false)
},
// 关闭人员选择弹窗
handleCancel() {
// 清空选中数据
this.searchText = ''
this.searchData = []
this.checkedKeys = []
// 关闭弹窗
this.$emit('personCancel', false)
},
onExpand(expandedKeys) {
this.expandedKeys = expandedKeys
this.autoExpandParent = false
},
// 树状图节点点击
onCheck(checkedKeys, nodes) {
this.searchData = []
nodes.checkedNodes.forEach((item) => {
// 只保留人员
if (!item.data.props.dataRef.children) {
this.searchData.push(item.data.props)
}
})
this.checkedKeys = checkedKeys
},
// 清除所有选中节点
clearAll() {
this.searchData = []
this.checkedKeys = []
},
// 清除当前选中节点
clearItem(id, index, record) {
this.searchData.splice(index, 1)
for (let i = 0; i < this.checkedKeys.length; i++) {
if (this.checkedKeys[i] == id) {
this.checkedKeys.splice(i, 1)
}
}
},
},
}
</script>
<style lang="less" scoped>
.treeNode {
height: 400px;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 6px;
height: 1px;
}
&::-webkit-scrollbar-thumb {
border-radius: 6px;
background: rgba(144, 147, 153, 0.5);
}
&::-webkit-scrollbar-track {
border-radius: 5px;
background: transparent;
}
}
.flex {
display: flex;
}
/deep/ .ant-card-body {
padding: 0;
}
/deep/ .ant-card-head {
background-color: #fff;
}
.selected_list {
flex: 1 1;
margin: 0 4px;
overflow: auto;
.selected_item {
height: 36px;
display: flex;
padding: 0 16px;
font-size: 14px;
cursor: pointer;
border-radius: 2px;
color: #111a34;
position: relative;
align-items: center;
justify-content: space-between;
&:hover {
background-color: #f7f8fb;
.selected_del {
display: block;
}
}
.selected_del {
display: none;
cursor: pointer;
transition: all 0.3s;
}
}
}
.group-footer {
display: flex;
margin-top: 30px;
justify-content: center;
}
</style>
antd-vue 人员选择组件
于 2023-08-22 10:08:01 首次发布