<template>
<el-dialog
:append-to-body="true"
title="配置人员"
v-if="hasLoaded"
:close-on-click-modal="false"
v-model="dialogVisible"
width="700px"
center
class="dialog"
@close="handleDialogVisible"
>
<div class="line"></div>
<el-form ref="form" :model="form" label-width="140px" size="default" :rules="rules">
<div>
活动名称:【活动ID] 活动名称
</div>
<!-- 树状选择区域 -->
<div class="merchant-select-area">
<div style="text-align: center;position: relative">
<h3>选择人员</h3>
<div class="selectablebox" style="margin-right: -2px">
<el-input
placeholder="请输入名称/ID"
v-model="merchantSearch"
style="margin-bottom: 10px; width: 220px"
class="merchant-search-input"
clearable
/>
<Search class="small-search-icon" />
<el-tree
ref="merchantTree"
:data="filteredTreeData"
show-checkbox
node-key="id"
:props="treeProps"
:filter-node-method="filterNode"
@check-change="handleTreeCheck"
default-expand-all
></el-tree>
</div>
</div>
<!-- 中间:穿梭箭头 -->
<div class="arrow-column">
<el-button class="btn" @click="moveToSelected" :disabled="selectedTreeNodes.length === 0">
<el-icon class="icons">
<ArrowRight />
<ArrowRight />
</el-icon>
</el-button>
<el-button class="btn" @click="moveToSelectable" :disabled="selectedMerchantsToRemove.length === 0">
<el-icon class="icons">
<ArrowLeft />
<ArrowLeft />
</el-icon>
</el-button>
</div>
<!-- 右侧:已选列表 -->
<div style="text-align: center" class="boxes">
<h3>已选中人员</h3>
<div class="selectablebox">
<div class="selected-column">
<el-checkbox-group v-model="selectedMerchantsToRemove">
<el-checkbox
v-for="merchant in selectedMerchants"
:key="merchant.id"
:label="merchant"
style="display: block; margin-bottom: 5px"
class="merchant-checkbox"
>
{{ merchant.name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
</div>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSave">保存</el-button>
</div>
</el-form>
</el-dialog>
</template>
<script>
import _ from 'lodash';
import { ElMessageBox, ElMessage } from 'element-plus';
import { Validator } from '/@/config/utils';
import { ArrowRight, ArrowLeft } from '@element-plus/icons-vue';
export default {
components: { ArrowRight, ArrowLeft },
props: {
id: {
type: String,
default: '',
},
activeId:{
type: String,
default: '',
}
},
data() {
return {
form: {
signatureTime: [],
templateName: '5',
},
dialogVisible: true,
hasLoaded: false,
merchantSearch: '',
treeData: [], // 原始树数据(包含所有节点)
filteredTreeData: [], // 过滤后显示的树数据(隐藏已选节点)
treeProps: { children: 'children', label: 'name' },
selectedMerchants: [], // 已选中的人员(右侧列表)
selectedMerchantsToRemove: [], // 右侧待移除的人员
selectedTreeNodes: [], // 左侧树中勾选的节点(待右移)
};
},
created() {
this.getDetail();
},
watch: {
merchantSearch(val) {
this.$refs.merchantTree.filter(val);
},
},
methods: {
async getDetail() {
this.$http.get(this.$api.settlement.promoteRateUserConfigDetail, {
params: { promoteId: this.id }// 传递费率模板ID
}).then(({ data }) => {
this.treeData = [...data.noMappingUserList]
this.selectedMerchants = [...(data.mappingUserList || [])]
this.filterTreeData();
this.hasLoaded = true;
}).catch(err => {
console.error('数据加载错误:', err)
})
},
// 过滤树数据(隐藏已选节点)
filterTreeData() {
const selectedIds = this.selectedMerchants.map((m) => m.id);
this.filteredTreeData = this.deepFilterTree(
JSON.parse(JSON.stringify(this.treeData)), // 深拷贝避免污染原始数据
selectedIds
);
},
deepFilterTree(tree, excludedIds) {
return tree
.map((node) => {
if (excludedIds.includes(node.id)) return null;
if (node.children && node.children.length > 0) {
const filteredChildren = this.deepFilterTree(node.children, excludedIds).filter((child) => child !== null);
if (filteredChildren.length > 0) {
return { ...node, children: filteredChildren };
}
return null; // 如果子节点都被过滤掉了,返回 null 表示该节点也需要被过滤
}
return node;
})
.filter((node) => node !== null);
},
// 树节点过滤方法(用于搜索)
filterNode(value, data) {
if (!value) return true;
return data.name.includes(value) || (data.id && data.id.includes(value));
},
// 树节点勾选变化
handleTreeCheck(data, checked) {
// 修复3:确保正确获取勾选的节点
if (this.$refs.merchantTree) {
const checkedNodes = this.$refs.merchantTree.getCheckedNodes(true);
this.selectedTreeNodes = checkedNodes.filter((node) => !node.children); // 只保留叶子节点(人员)
}
},
// 右移:选中节点到右侧
moveToSelected() {
if (this.selectedTreeNodes.length === 0) return;
// 合并到已选列表(去重)
this.selectedMerchants = _.unionBy(this.selectedMerchants, this.selectedTreeNodes, 'id');
// 取消树中勾选 & 重新过滤树(隐藏已选节点)
this.selectedTreeNodes.forEach((node) => {
if (this.$refs.merchantTree) {
this.$refs.merchantTree.setChecked(node.id, false, true);
}
});
this.selectedTreeNodes = [];
this.filterTreeData();
},
// 左移:从右侧移除节点
moveToSelectable() {
if (this.selectedMerchantsToRemove.length === 0) return;
// 从已选列表移除
this.selectedMerchants = this.selectedMerchants.filter((m) => !this.selectedMerchantsToRemove.some((rm) => rm.id === m.id));
// 清空右侧待移除勾选 & 重新过滤树(显示移回的节点)
this.selectedMerchantsToRemove = [];
this.filterTreeData(); // 刷新树(显示移回的节点)
},
handleSave() {
let vm = this;
let { form } = this;
var validator = new Validator();
validator.add(vm.selectedMerchants, [{ strategy: 'isNonEmpty', errorMsg: '请至少选择一个人员' }]);
const errorMsg = validator.check();
if (errorMsg) return ElMessage.error(errorMsg);
console.log('最终选中的人员:', this.selectedMerchants);
const submitData = {
activeId:this.activeId,
userIds:this.selectedMerchants.map(item => item.userId) // 已选中的人员数组
};
console.log("88888888888",submitData);
this.$http.post(this.$api.settlement.comASave, submitData)
.then(( data ) => {
console.log('配置已保存:', data)
if (data.code == 0) {
this.$message.success('配置已保存')
this.$emit('onSuccess', submitData)
this.dialogVisible = false
} else {
this.$message.error(data.msg || '保存失败')
}
})
.catch(err => {
//this.$message.error('保存失败,请重试')
console.error('保存错误:', err)
})
},
handleDialogVisible() {
this.$emit('onClose');
},
},
};
</script>
<style lang="scss" scoped>
::v-deep .el-input {
width: 350px;
}
.dialog-footer {
display: flex;
justify-content: center;
margin-top: 20px;
}
.line {
margin-top: -20px;
margin-bottom: 23px;
border-bottom: 1px solid #3333 !important;
}
::v-deep .el-dialog__title {
text-align: center;
font-size: 18px;
}
::v-deep .el-date-editor {
min-width: 350px !important;
}
.merchant-select-area {
display: flex;
justify-content: space-between;
margin-top: 43px;
max-height: 400px;
overflow: hidden;
}
.boxes {
margin-bottom: 10px;
display: flex;
flex-direction: column;
align-items: center;
width: 26-px;
}
.selectablebox {
margin-top: 10px;
width: 260px;
border: 1px solid #3333;
border-radius: 5px;
padding: 10px;
height: 350px;
}
.selectablebox > div {
width: 100%;
}
.arrow-column {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.arrow-column .el-button {
margin: 5px 0;
}
/* 树组件样式 */
::v-deep .el-tree {
width: 100%;
max-height: 350px;
overflow-y: auto;
padding: 10px;
}
/* 已选列表样式 */
.selected-column {
flex: 1;
width: 90%;
max-height: 350px;
overflow-y: auto;
border-radius: 5px;
// padding: 10px;
margin-top: 10px;
}
/* 优化滚动条 */
::v-deep .el-tree::-webkit-scrollbar,
.selected-column::-webkit-scrollbar {
width: 6px;
}
::v-deep .el-tree::-webkit-scrollbar-thumb,
.selected-column::-webkit-scrollbar-thumb {
background-color: #ddd;
border-radius: 3px;
}
.merchant-search-input ::v-deep .el-input__wrapper {
border-radius: 30px;
}
.merchant-checkbox ::v-deep.el-checkbox {
// border: 1px solid #3333;
width: 100%;
// box-sizing: border-box;
// word-wrap: break-word;
// word-break: break-all;
height: 37px;
line-height: 37px;
padding-left: 10px;
margin-top: 10px;
}
.btn {
color: var(--el-color-primary);
border: 1px solid var(--el-color-primary);
}
:deep(.small-search-icon) {
font-size: 12px !important;
color: #666 !important;
cursor: pointer;
position: absolute;
top: 52px;
right: 69px;
width: 12px !important;
height: 12px !important;
}
:deep(.el-checkbox-group) {
display: flex !important;
flex-wrap: wrap;
}
:deep(.el-checkbox-group .el-checkbox) {
width: 47px !important;
box-sizing: border-box;
}
:deep(.el-button:not(.is-circle) i.el-icon),
:deep(.el-button i.fa),
:deep(.el-button--default i.iconfont),
:deep(.el-button--default i.fa) {
font-size: 32px !important;
}
</style>
树形结构移动到右边将父节点也要带过去,将父节点移动到右边同时移动父节点下面的所有数据到左边