|
@@ -0,0 +1,337 @@
|
|
|
+<template>
|
|
|
+ <el-dialog
|
|
|
+ :title="title"
|
|
|
+ :visible.sync="visible"
|
|
|
+ :width="dialogWidth"
|
|
|
+ :before-close="handleClose"
|
|
|
+ append-to-body
|
|
|
+
|
|
|
+ class="tree-copy-modal"
|
|
|
+ >
|
|
|
+ <!-- 提示信息 -->
|
|
|
+ <div class="alert-message">
|
|
|
+ <span>温馨提示:允许单对多、多对单、多对多的关联关系。关联后可在对应节点查看。【关联并继续】可以继续续进行关联。</span>
|
|
|
+
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 树形结构容器 -->
|
|
|
+ <div class="tree-container">
|
|
|
+ <!-- 左侧复制源树 -->
|
|
|
+ <div class="tree-wrapper left-tree">
|
|
|
+ <div class="tree-title">复制源</div>
|
|
|
+ <el-tree
|
|
|
+ ref="sourceTree"
|
|
|
+ :data="sourceTreeData"
|
|
|
+ :props="treeProps"
|
|
|
+ :load="loadSourceNode"
|
|
|
+ node-key="id"
|
|
|
+ show-checkbox
|
|
|
+ lazy
|
|
|
+ :expand-on-click-node="false"
|
|
|
+ @check="handleSourceCheck"
|
|
|
+ ></el-tree>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 中间箭头 -->
|
|
|
+ <div class="tree-arrow">
|
|
|
+ <i class="el-icon-arrow-right"></i>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 右侧复制到树 -->
|
|
|
+ <div class="tree-wrapper right-tree">
|
|
|
+ <div class="tree-title">复制到</div>
|
|
|
+ <el-tree
|
|
|
+ ref="targetTree"
|
|
|
+ :data="targetTreeData"
|
|
|
+ :props="treeProps"
|
|
|
+ :load="loadTargetNode"
|
|
|
+ node-key="id"
|
|
|
+ show-checkbox
|
|
|
+ lazy
|
|
|
+ :expand-on-click-node="false"
|
|
|
+ @check="handleTargetCheck"
|
|
|
+ ></el-tree>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 底部按钮 -->
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button @click="handleClose">取消</el-button>
|
|
|
+ <el-button type="primary" @click="handleLinkAndExit">关联并退出</el-button>
|
|
|
+ <el-button type="success" @click="handleLinkAndContinue">关联并继续</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ getLazytree,
|
|
|
+
|
|
|
+} from "@/api/manager/wbstree";
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'TreeCopyModal',
|
|
|
+ props: {
|
|
|
+ // 弹窗标题
|
|
|
+ title: {
|
|
|
+ type: String,
|
|
|
+ default: '复制节点'
|
|
|
+ },
|
|
|
+ // 弹窗宽度
|
|
|
+ dialogWidth: {
|
|
|
+ type: String,
|
|
|
+ default: '80%'
|
|
|
+ },
|
|
|
+ // 源树和目标树的ID参数
|
|
|
+ sourceId: {
|
|
|
+ type: [String, Number],
|
|
|
+ required: true
|
|
|
+ },
|
|
|
+ targetId: {
|
|
|
+ type: [String, Number],
|
|
|
+ required: true
|
|
|
+ },
|
|
|
+ // 用户信息,包含tenant_id
|
|
|
+ userInfo: {
|
|
|
+ type: Object,
|
|
|
+ required: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ // 弹窗显示状态
|
|
|
+ visible: false,
|
|
|
+ // 源树数据
|
|
|
+ sourceTreeData: [],
|
|
|
+ // 目标树数据
|
|
|
+ targetTreeData: [],
|
|
|
+ // 树的配置项
|
|
|
+ treeProps: {
|
|
|
+ children: "children",
|
|
|
+ label: "title",
|
|
|
+ isLeaf: function (data) {
|
|
|
+ if (data.hasChildren && data.isExistForm != 1) {
|
|
|
+ return false;
|
|
|
+ } else if (data.hasChildren && data.isExistForm == 1) {
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ },
|
|
|
+
|
|
|
+ // 选中的源节点
|
|
|
+ selectedSourceNodes: [],
|
|
|
+ // 选中的目标节点
|
|
|
+ selectedTargetNodes: []
|
|
|
+ };
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 显示弹窗
|
|
|
+ show() {
|
|
|
+ this.visible = true;
|
|
|
+ // 重置选中状态
|
|
|
+ this.selectedSourceNodes = [];
|
|
|
+ this.selectedTargetNodes = [];
|
|
|
+ // 重置树
|
|
|
+ if (this.$refs.sourceTree) {
|
|
|
+ this.$refs.sourceTree.setCheckedKeys([]);
|
|
|
+ }
|
|
|
+ if (this.$refs.targetTree) {
|
|
|
+ this.$refs.targetTree.setCheckedKeys([]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 关闭弹窗
|
|
|
+ handleClose() {
|
|
|
+ this.visible = false;
|
|
|
+ this.$emit('close');
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载源树节点
|
|
|
+ loadSourceNode(node, resolve) {
|
|
|
+ let pid = 0;
|
|
|
+ if (node.level !== 0) {
|
|
|
+ pid = node.data.id;
|
|
|
+ }
|
|
|
+ getLazytree(this.sourceId, pid, this.userInfo.tenant_id)
|
|
|
+
|
|
|
+ .then(res => {
|
|
|
+ let arr = [];
|
|
|
+ if (Array.isArray(res.data.data)) {
|
|
|
+ arr = res.data.data.map(item => ({
|
|
|
+ ...item,
|
|
|
+ // 如果后端没有返回isLeaf,这里可以根据实际业务逻辑判断
|
|
|
+ isLeaf: item.isLeaf !== undefined ? item.isLeaf : false
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ resolve(arr);
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ console.error('加载源树节点失败:', err);
|
|
|
+ resolve([]);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载目标树节点
|
|
|
+ loadTargetNode(node, resolve) {
|
|
|
+ let pid = 0;
|
|
|
+ if (node.level !== 0) {
|
|
|
+ pid = node.data.id;
|
|
|
+ }
|
|
|
+ getLazytree(this.sourceId, pid, this.userInfo.tenant_id)
|
|
|
+ .then(res => {
|
|
|
+ let arr = [];
|
|
|
+ if (Array.isArray(res.data.data)) {
|
|
|
+ arr = res.data.data.map(item => ({
|
|
|
+ ...item,
|
|
|
+ isLeaf: item.isLeaf !== undefined ? item.isLeaf : false
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ resolve(arr);
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ console.error('加载目标树节点失败:', err);
|
|
|
+ resolve([]);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理源树节点选择
|
|
|
+ handleSourceCheck(data, checked, indeterminate) {
|
|
|
+ this.selectedSourceNodes = this.$refs.sourceTree.getCheckedNodes(true, false);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理目标树节点选择
|
|
|
+ handleTargetCheck(data, checked, indeterminate) {
|
|
|
+ this.selectedTargetNodes = this.$refs.targetTree.getCheckedNodes(true, false);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 显示关联规则提示
|
|
|
+ showLinkTips() {
|
|
|
+ this.$alert('关联规则说明:\n1. 允许单对单、多对单、多对多的关联关系\n2. 关联后可在对应节点查看关联信息\n3. 复制操作不会影响原节点数据', '关联规则', {
|
|
|
+ confirmButtonText: '确定'
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 关联并退出
|
|
|
+ handleLinkAndExit() {
|
|
|
+ this.handleLink(true);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 关联并继续
|
|
|
+ handleLinkAndContinue() {
|
|
|
+ this.handleLink(false);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理关联操作
|
|
|
+ handleLink(exitAfterLink) {
|
|
|
+ if (this.selectedSourceNodes.length === 0 || this.selectedTargetNodes.length === 0) {
|
|
|
+ this.$message.warning('请至少选择一个源节点和一个目标节点');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 准备关联数据
|
|
|
+ const linkData = {
|
|
|
+ sourceNodes: this.selectedSourceNodes.map(node => node.id),
|
|
|
+ targetNodes: this.selectedTargetNodes.map(node => node.id)
|
|
|
+ };
|
|
|
+
|
|
|
+ // 触发父组件的关联事件
|
|
|
+ this.$emit('link', linkData, () => {
|
|
|
+ this.$message.success('关联成功');
|
|
|
+ if (exitAfterLink) {
|
|
|
+ this.handleClose();
|
|
|
+ } else {
|
|
|
+ // 清空选择但不关闭弹窗
|
|
|
+ this.$refs.sourceTree.setCheckedKeys([]);
|
|
|
+ this.$refs.targetTree.setCheckedKeys([]);
|
|
|
+ this.selectedSourceNodes = [];
|
|
|
+ this.selectedTargetNodes = [];
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.tree-copy-modal {
|
|
|
+ .alert-message {
|
|
|
+ padding: 10px 15px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-left: 4px solid orange;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 14px;
|
|
|
+ color: orange;
|
|
|
+
|
|
|
+ .el-icon-info-circle {
|
|
|
+ color: #409eff;
|
|
|
+ margin-right: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ margin-left: auto;
|
|
|
+ color: #409eff;
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .tree-container {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ height: 500px;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tree-wrapper {
|
|
|
+ flex: 1;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .tree-title {
|
|
|
+ padding: 10px 15px;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-bottom: 1px solid #e4e7ed;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-tree {
|
|
|
+ flex: 1;
|
|
|
+ overflow: auto;
|
|
|
+ padding: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .left-tree {
|
|
|
+ margin-right: 15px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .right-tree {
|
|
|
+ margin-left: 15px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tree-arrow {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 40px;
|
|
|
+ color: #c0c4cc;
|
|
|
+
|
|
|
+ .el-icon-arrow-right {
|
|
|
+ font-size: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .dialog-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ padding-top: 15px;
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|