
<script setup>
import {ref, reactive, getCurrentInstance, watch} from 'vue'
import {ElLoading, ElMessage} from 'element-plus'
import {addOrgUser, listDeptTree, listOrgUser} from "../../api/gljg";
const visible = ref(false)
const title = ref('')
const formRef = ref(null)
const leftTreeRef = ref(null)
const rightTreeRef = ref(null)
const leftTreeData = ref([])
const rightTreeData = ref([])
const allUsers = ref([])
const form = ref({
orgId: null,
userList: [],
})
const leftFilterText = ref('')
const rightFilterText = ref('')
const rules = reactive({})
const {proxy} = getCurrentInstance();
const emit = defineEmits(['success'])
const treeProps = {
label: 'label',
children: 'children',
disabled:'disabled'
}
function processTreeData(data) {
return data.map(item => {
const processedItem = {
realId: item.realId,
label: item.label,
treeType: item.treeType,
children: item.children&&item.children.length>0 ? processTreeData(item.children) : undefined
}
if (item.treeType === '1') {
processedItem.label = `📁 ${item.label}`
}
return processedItem
})
}
function filterNode(value, data) {
if (!value) return true
return data.label.toLowerCase().includes(value.toLowerCase())
}
watch(leftFilterText, (val) => {
leftTreeRef.value?.filter(val)
})
watch(rightFilterText, (val) => {
rightTreeRef.value?.filter(val)
})
async function getList() {
try {
leftTreeData.value = []
allUsers.value = []
const res = await listDeptTree()
if (res.code === 200) {
allUsers.value = processTreeData(res.data)
listHasUser()
}
} catch (error) {
proxy.$modal.msgError("获取列表失败:" + error.message)
}
}
async function listHasUser() {
try {
form.value.userList = []
let query = {
orgId: form.value.orgId
}
const res = await listOrgUser(query)
if (res.code === 200) {
form.value.userList = res.data.map(item => item.realId)
updateLeftTreeData()
updateRightTreeData()
}
} catch (error) {
proxy.$modal.msgError("获取列表失败:" + error.message)
}
}
const addSelectedUsers = () => {
const checkedNodes = leftTreeRef.value.getCheckedNodes(false, false)
const allCheckedKeys = []
checkedNodes.forEach(node => {
if (node.treeType !== '1') {
allCheckedKeys.push(node.realId)
} else {
collectUserIds(node, allCheckedKeys)
}
})
if (allCheckedKeys.length === 0) {
ElMessage.warning('请先选择要分配的人员')
return
}
const newUserList = [...new Set([...form.value.userList, ...allCheckedKeys])]
form.value.userList = newUserList
leftTreeRef.value.setCheckedKeys([])
updateLeftTreeData()
updateRightTreeData()
}
function collectUserIds(node, realIds) {
if (node.treeType !== '1') {
realIds.push(node.realId)
}
if (node.children) {
node.children.forEach(child => collectUserIds(child, realIds))
}
}
function updateLeftTreeData() {
leftTreeData.value = filterTreeData(JSON.parse(JSON.stringify(allUsers.value)))
}
function filterTreeData(nodes) {
return nodes.map(node => {
if (node.treeType !== '1' && form.value.userList.includes(node.realId)) {
node.disabled = true
}
if (node.children) {
node.children = filterTreeData(node.children)
}
return node
})
}
function updateRightTreeData() {
rightTreeData.value = buildAssignedTree(JSON.parse(JSON.stringify(allUsers.value)))
}
function buildAssignedTree(nodes) {
const result = []
nodes.forEach(node => {
if (node.treeType !== '1' && form.value.userList.includes(node.realId)) {
result.push({...node})
}
else if (node.children) {
const assignedChildren = buildAssignedTree(node.children)
if (assignedChildren.length > 0) {
const newNode = {...node,children: assignedChildren}
result.push(newNode)
}
}
})
return result
}
const removeSelectedUsers = () => {
const checkedNodes = rightTreeRef.value.getCheckedNodes(false, false)
const checkedKeys = []
checkedNodes.forEach(node => {
if (node.treeType !== '1') {
checkedKeys.push(node.realId)
} else {
collectUserIds(node, checkedKeys)
}
})
if (checkedKeys.length === 0) {
ElMessage.warning('请先选择要移除的人员')
return
}
form.value.userList = form.value.userList.filter(realId => !checkedKeys.includes(realId))
rightTreeRef.value.setCheckedKeys([])
updateLeftTreeData()
updateRightTreeData()
}
const removeAllUsers = () => {
if (form.value.userList.length === 0) {
ElMessage.warning('暂无已分配人员')
return
}
form.value.userList = []
updateLeftTreeData()
updateRightTreeData()
}
const show = (orgId) => {
reset()
visible.value = true
title.value = "修改人员权限"
form.value.orgId = orgId
try {
getList()
} catch (error) {
proxy.$modal.msgError("获取详情失败:" + error.message)
}
}
const cancel = () => {
visible.value = false
reset()
}
const reset = () => {
form.value = {
orgId: null,
userList: [],
}
leftFilterText.value = ''
rightFilterText.value = ''
leftTreeData.value = []
rightTreeData.value = []
allUsers.value = []
formRef.value?.resetFields()
}
const submitForm = async () => {
if (!formRef.value) return
const loading = ElLoading.service({
lock: true,
text: '提交中...',
background: 'rgba(0, 0, 0, 0.7)',
})
try {
let validRes = await formRef.value.validate()
if (validRes) {
if (form.value.orgId != null) {
let res = await addOrgUser(form.value)
if (res.code === 200) {
ElMessage({
message: res.msg,
type: 'success'
})
emit('getList')
visible.value = false
} else {
ElMessage({
message: res.msg,
type: 'error'
})
}
}
}
loading.close()
} catch (error) {
console.error('提交表单失败:', error)
loading.close()
}
}
defineExpose({
show
})
</script>
<template>
<div>
<el-dialog
:title="title"
v-model="visible"
width="1200px"
append-to-body
custom-class="industry-code-dialog"
>
<el-form ref="formRef" :model="form" :rules="rules">
<div class="transfer-container">
<div class="left-panel">
<div class="panel-header">
<span>系统人员</span>
<el-input
v-model="leftFilterText"
placeholder="搜索人员"
clearable
size="small"
style="width: 200px; margin-left: 10px;"
/>
</div>
<el-tree ref="leftTreeRef" :data="leftTreeData" node-key="realId" show-checkbox :default-checked-keys="form.userList" :default-expanded-keys="form.userList"
:props="treeProps" :filter-node-method="filterNode" class="tree-container"/>
</div>
<div class="center-buttons">
<el-button type="primary" icon="el-icon-arrow-right" @click="addSelectedUsers">分配 ></el-button>
<el-button type="primary" icon="el-icon-arrow-left" @click="removeSelectedUsers">< 移除</el-button>
<el-button type="danger" icon="el-icon-delete" @click="removeAllUsers">全部移除</el-button>
</div>
<div class="right-panel">
<div class="panel-header">
<span>已选人员 ({{ form.userList.length }})</span>
<el-input
v-model="rightFilterText"
placeholder="搜索人员"
clearable
size="small"
style="width: 200px; margin-left: 10px;"
/>
</div>
<el-tree ref="rightTreeRef" :data="rightTreeData" node-key="realId" show-checkbox :default-expanded-keys="form.userList"
:props="treeProps" :filter-node-method="filterNode" class="tree-container"/>
</div>
</div>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">取 消</el-button>
<el-button type="primary" @click="submitForm">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped>
.industry-code-dialog :deep(.el-dialog__body) {
box-sizing: border-box;
padding: 20px 30px;
height: 100%;
overflow-y: auto;
}
.transfer-container {
display: flex;
height: 500px;
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
}
.left-panel, .right-panel {
flex: 1;
display: flex;
flex-direction: column;
border-right: 1px solid #ebeef5;
}
.right-panel {
border-right: none;
}
.panel-header {
padding: 12px 15px;
background-color: #f5f7fa;
border-bottom: 1px solid #ebeef5;
font-weight: bold;
display: flex;
align-items: center;
justify-content: space-between;
}
.tree-container {
flex: 1;
padding: 10px;
overflow: auto;
}
.center-buttons {
width: 100px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #fafafa;
padding: 0 10px;
}
.center-buttons .el-button {
margin: 10px 0;
width: 100%;
}
.selected-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.empty-tip {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 14px;
}
.scroll-container {
flex: 1;
padding: 10px;
height: 0;
}
.selected-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
margin-bottom: 8px;
background-color: #f5f7fa;
border-radius: 4px;
transition: all 0.3s;
}
.selected-item:hover {
background-color: #ecf5ff;
transform: translateY(-1px);
}
.item-label {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.remove-btn {
padding: 0;
color: #f56c6c;
opacity: 0.7;
}
.remove-btn:hover {
opacity: 1;
}
.dialog-footer {
text-align: right;
margin-top: 20px;
}
.dialog-footer .el-button {
min-width: 100px;
padding: 10px 20px;
}
.dialog-footer .el-button + .el-button {
margin-left: 12px;
}
.dialog-footer .el-button:hover {
opacity: 0.9;
transform: translateY(-1px);
transition: all 0.3s ease;
}
</style>