田源
2024-04-18 b8d7b6603f8d5018655e4e6f8098be90ca443e17
Source/UBCS-WEB/src/views/integration/integrationIndex.vue
@@ -1,12 +1,12 @@
<template>
  <div>
    <el-container style="height: 100%; border: 1px solid #fff">
      <el-card style="margin-right: 10px;height:  calc(100vh - 125px)">
        <el-aside style="background-color: #fff" width="210px">
      <el-card style="margin-right: 10px;height:  calc(100vh - 125px);overflow: auto">
        <el-aside style="background-color: #fff;" width="210px">
          <el-input v-model="filterText" placeholder="输入关键字进行过滤">
          </el-input>
          <el-menu :default-openeds="['1', '3']" >
            <el-tree  ref="tree" :data="treeData" :filter-node-method="filterNode" :props="defaultProps"
          <el-menu :default-openeds="['1', '3']">
            <el-tree ref="tree" :data="treeData" :filter-node-method="filterNode" :props="defaultProps"
                     accordion class="filter-tree" empty-text="暂无数据" @node-click="handelTreeCell">
              <template slot-scope="{ node, data }" class="el-tree-node__label">
                <el-tooltip :content="$createElement('div', { domProps: { innerHTML: node.label } })" class="item"
@@ -22,45 +22,67 @@
          </el-menu>
        </el-aside>
      </el-card>
      <el-main style="height: calc(100vh - 125px)">
        <el-card>
      <el-main>
        <el-card style="height: calc(100vh - 128px)">
          <el-form :model="form">
            <el-form-item label="集团分类" label-width="80px" size="small">
              <el-select ref="selectTree" v-model="groupVal" clearable placeholder="请选择"
                         popper-class="popperTreeSelect">
                <el-option :label="groupVal" :value="groupVal">
                  <el-tree ref="groupTree" :data="groupTreeData" :props="defaultProps" empty-text="暂无数据"
                           @node-click="handleNodeClick">
                <el-option :disabled="true" :label="groupVal" :value="groupVal">
                  <el-tree ref="groupTree"
                           :data="groupTreeData"
                           :default-checked-keys="defaultCheckedKeys"
                           :default-expanded-keys="defaultCheckedKeys"
                           :props="defaultProps"
                           empty-text="暂无数据"
                           node-key="oid"
                           show-checkbox
                           @check="handleCheck">
                  </el-tree>
                </el-option>
              </el-select>
            </el-form-item>
          </el-form>
          <el-card  style="height:calc(100vh - 600px)">
          <el-card style="height:38vh">
            <avue-crud ref="crudMapping" :data="mappingData" :option="optionMapping" :table-loading="loading"
                       @select="setCurrentRow" @row-update="handleMapingUpdate"
                       @row-click="handleMapingClick" @row-dblclick="handleMapingRowClick" @selection-change="selectionChange"
                       @row-click="handleMapingClick" @row-dblclick="handleMapingRowClick"
                       @selection-change="selectionChange"
                       @select-all="handleSelectAll">
              <template slot="radio"
                        slot-scope="{row}">
                <el-radio v-model="selectRow" :label="row.$index">-</el-radio>
              </template>
              <template slot="menuLeft">
                <el-button :disabled="disabledPush" icon="el-icon-plus" size="small" type="primary"
                           @click="dialogPush = true">新 增
                <!--                <el-button :disabled="disabledPush" icon="el-icon-plus" size="small" type="primary"-->
                <!--                           @click="dialogPush = true">新 增-->
                <!--                </el-button>-->
                <el-button v-if="permissionList.saveBtn" icon="el-icon-check" size="small" type="primary"
                           @click="handleSave">保 存
                </el-button>
                <el-button icon="el-icon-check" size="small" type="primary" @click="handleSave">保 存
                <el-button v-if="permissionList.allSyncBtn" icon="el-icon-connection" size="small"
                           type="primary" @click="handleSync('all')">同步主模型
                </el-button>
                <el-button icon="el-icon-connection" size="small" type="primary"
                           @click="handleSync('all')">同步主模型
                <el-button v-if="permissionList.oneSyncBtn" icon="el-icon-connection" size="small"
                           type="primary" @click="handleSync('one')">同步详细模型
                </el-button>
                <el-button icon="el-icon-connection" size="small" type="primary"
                           @click="handleSync('one')">同步详细模型
                <el-button v-if="permissionList.autoBtn" icon="el-icon-coordinate" size="small"
                           type="primary" @click="handlerAuto">自动填充
                </el-button>
              </template>
            </avue-crud>
          </el-card>
          <el-card style="margin-top: 20px;height: calc(100vh - 575px)">
          <el-card style="margin-top: 10px;height: 38vh">
            <avue-crud ref="crudRange" :data="rangeData" :option="optinoRange" :style="{ marginTop: '-20px'}"
                       @row-update="handleUpdate"
                       @row-dblclick="handleRowClick">
              <template slot="menuLeft">
                <el-button v-if="permissionList.bottomAutoBtn" icon="el-icon-coordinate" size="small"
                           type="primary" @click="handlerBottomAuto">自动填充
                </el-button>
              </template>
            </avue-crud>
          </el-card>
        </el-card>
@@ -73,6 +95,7 @@
<script>
import integrationTransfer from './integrationTransfer.vue'
import pinyin from 'js-pinyin'
import {mapGetters} from "vuex";
import {
  referCodeClassifyTree,
  referTree,
@@ -82,6 +105,7 @@
  listCodeAttributeByClassId,
  syncClassifyModel
} from '@/api/integration/integration.js'
import {getByGroupAttrMapping, getEnumAttrByClsOidAndAttrId} from "@/api/vciAttrbute";
export default {
  components: {
@@ -105,6 +129,10 @@
      highlightCurrentRow: true,
    }
    return {
      selectRow: "",
      targetNameList: [], // 元数据返回名称
      defaultCheckedKeys: [],
      isNodeDisabled: true,
      // 表单值
      form: {
        // 集团树显示值
@@ -128,8 +156,8 @@
      optinoRange: {
        title: '属性映射取值范围',
        maxHeight: '280px',
        header: false,
        rowKey: 'oid',
        refreshBtn: false,
        column: [
          {label: '属性集团枚举值', prop: 'numTextValue', minWidth: 80},
          {label: '集团属性枚举显示名称', prop: 'numText', minWidth: 80},
@@ -152,11 +180,25 @@
      },
      // 属性映射表配置
      optionMapping: {
        maxHeight: '250px',
        maxHeight: '280px',
        header: true,
        rowKey: 'oid',
        selection: false,
        refreshBtn: false,
        delBtn: false,
        addBtn: false,
        columnBtn: false,
        searchShow: true,
        emptyBtn: false,
        searchBtn: false,
        searchShowBtn: false,
        cellBtn: true,
        border: true,
        searchMenuSpan: 8,
        highlightCurrentRow: true,
        $cellEdit: true,
        column: [
          {label: '', prop: 'radio', width: 60, hide: false},
          {label: '集团分类', prop: 'sourceClassifyName', minWidth: 80},
          {label: '所属视图', prop: 'viewName', minWidth: 80},
          {label: '集团属性', prop: 'sourceAttrName', minWidth: 80},
@@ -164,28 +206,38 @@
          {
            label: '属性名称',
            prop: 'targetAttrName',
            minWidth: 80,
          },
          {
            label: '默认值',
            prop: 'defaultValue',
            minWidth: 140,
            type: 'select',
            cell: true,
            blur: (value) => window.handleBlur(value, 'mapping')
            filterable: true,
            minWidth: 80,
            props: {
              label: 'targetAttrName',
              value: 'targetAttrName'
            },
            dicData: [],
          },
          // {
          //   label: '默认值',
          //   prop: 'defaultValue',
          //   minWidth: 140,
          //   cell: true,
          //   blur: (value) => window.handleBlur(value, 'mapping')
          // },
        ],
        ...options
      },
      // 主数据分类树
      treeData: [],
      // 集团分类树
      groupTreeData: [],
      // 树popos替换值
      // 树prpos替换值
      defaultProps: {
        children: 'children',
        label: 'name',
        id: 'oid',
        isLeaf: ''
        isLeaf: '',
        // disabled:()=>{
        //   return true
        // }
      },
      transferProps: {
        key: 'oid',
@@ -201,6 +253,8 @@
      mappingForm: {},
      // 定时器
      times: null,
      TreeOid: "",
      tableSelectId: ''
    }
  },
@@ -216,7 +270,6 @@
  mounted() {
    let that = this
    window.handleBlur = that.handleBlur
  },
  computed: {
    filtermapping() {
@@ -227,9 +280,127 @@
        return obj.targetAttrName
      })
      return transfer
    }
    },
    targetColumn() {
      return this.optionMapping.column.find(column => column.prop === 'targetAttrName');
    },
    ...mapGetters(["permission"]),
    permissionList() {
      return {
        allSyncBtn: this.vaildData(this.permission.integration.integration_allSync, false),
        autoBtn: this.vaildData(this.permission.integration.integration_auto, false),
        bottomAutoBtn: this.vaildData(this.permission.integration.integration_bottomAuto, false),
        oneSyncBtn: this.vaildData(this.permission.integration.integration_oneSync, false),
        saveBtn: this.vaildData(this.permission.integration.integration_save, false),
      }
    },
  },
  methods: {
    handlerAuto() {
      if (!this.TreeOid || this.TreeOid === "") {
        return;
      }
      // 过滤出集团属性的key值
      const groupArray = this.mappingData ? this.mappingData.map(obj => obj.sourceAttrKey) : [];
      // 获取到需要自动填充的值
      getByGroupAttrMapping({classifyId: this.TreeOid, groupAttrKeyList: groupArray})
        .then(res => {
          //  单独把返回值的groupAttrKey放一个数组,然后过滤出返回值的groupAttrKey是否等于表格数据中的sourceAttrKey
          // const groupReturnData = res.data && res.data.data ? res.data.data.map(item => item.groupAttrKey) : [];
          const groupReturnData = res.data.data;
          // 过滤匹配
          this.mappingData.forEach(mappingItem => {
            groupReturnData.forEach(groupItem => {
              if (mappingItem.sourceAttrKey === groupItem.groupAttrKey) {
                const result = this.transferData.find(obj => obj.id.toLowerCase() === groupItem.codeMetaAttrKey.toLowerCase());
                if (result) {
                  mappingItem.targetAttrId = groupItem.codeMetaAttrOid.toLowerCase();
                  mappingItem.targetAttrName = groupItem.codeMetaAttrName.toLowerCase();
                  mappingItem.targetAttrKey = groupItem.codeMetaAttrKey.toLowerCase();
                } else {
                  mappingItem.targetAttrId = '';
                  mappingItem.targetAttrName = '';
                  mappingItem.targetAttrKey = '';
                }
              }
            })
          })
          this.$message.success('自动填充成功,请确认属性后点击保存!');
        })
        .catch(error => {
          this.$message.error('填充失败,请稍后再试!');
        });
    },
    handlerBottomAuto() {
      if (this.rangeData.length <= 0) {
        this.$message.warning('请选择一条枚举类型属性!')
        return
      }
      getEnumAttrByClsOidAndAttrId({classifyId: this.TreeOid, codeMetaAttrKey: this.tableSelectId}).then(res => {
        const data = res.data.data;
        this.rangeData.forEach(rangeItem => {
          data.forEach(dataItem => {
            const similarity = this.calculateSimilarity(rangeItem.numText, dataItem.itemName);
            if (similarity > 70) {
              rangeItem.targetNumTextValue = rangeItem.numTextValue;
              rangeItem.targetNumText = dataItem.itemName;
            }
          })
        })
        this.$message.success('自动填充成功,请确认属性后点击保存!')
      })
    },
    calculateSimilarity(str1, str2) {
      // 计算编辑距离
      function editDistance(s1, s2) {
        s1 = s1.toLowerCase();
        s2 = s2.toLowerCase();
        const costs = [];
        for (let i = 0; i <= s1.length; i++) {
          let lastValue = i;
          for (let j = 0; j <= s2.length; j++) {
            if (i === 0)
              costs[j] = j;
            else {
              if (j > 0) {
                let newValue = costs[j - 1];
                if (s1.charAt(i - 1) !== s2.charAt(j - 1))
                  newValue = Math.min(Math.min(newValue, lastValue),
                    costs[j]) + 1;
                costs[j - 1] = lastValue;
                lastValue = newValue;
              }
            }
          }
          if (i > 0)
            costs[s2.length] = lastValue;
        }
        return costs[s2.length];
      }
      // 计算相似度百分比
      function similarityPercent(s1, s2) {
        let maxLength = Math.max(s1.length, s2.length);
        let distance = editDistance(s1, s2);
        return ((maxLength - distance) / maxLength) * 100;
      }
      // 调用相似度计算函数并返回百分比形式的相似度
      const similarity = similarityPercent(str1, str2);
      return similarity;
    },
    getTargetName(data) {
      this.targetColumn.dicData = data.filter(item => item.name && item.name.trim() !== "") // 过滤掉name为空的属性
        .map(item => {
          return {
            targetAttrId: item.oid,
            targetAttrKey: item.id,
            targetAttrName: item.name,
            // disabled: false
          }
        });
    },
    // 接口左侧树
    async getReferCodeClassifyTree() {
      this.treeData = []
@@ -253,7 +424,13 @@
      if (response.status === 200) {
        this.loading = false
        this.mappingData = response.data.data
        // console.log(this.filtermapping)
        // for (const item of this.mappingData) {
        //   if (item.targetAttrName && item.targetAttrId && item.targetAttrKey) {
        //     const targetObject = this.targetColumn.dicData.find(obj => obj.targetAttrName === item.targetAttrName);
        //     targetObject.disabled = true;
        //   }
        // }
      }
    },
    // 接口获取属性映射取值范围
@@ -261,7 +438,6 @@
      this.rangeData = []
      const response = await gridAttrRanges({meatId: oid})
      if (response.status === 200) {
        // console.log(response.data)
        this.rangeData = response.data.data
      }
    },
@@ -272,29 +448,86 @@
      const response = await listCodeAttributeByClassId({codeClassifyId: oid})
      if (response.status === 200) {
        const data = response.data.data
        this.transferData = data
        this.transferData = data;
        this.getTargetName(data)
        // console.log(data)
      }
    },
    // 接口获取集团分类树
    async referTree(oid, checked) {
      this.groupTreeData = []
      const response = await referTree({'conditionMap[codeclsfid]': oid, parentOid:'0'})
    async referTree(oid) {
      this.groupTreeData = [];
      const response = await referTree({'conditionMap[codeclsfid]': oid, parentOid: '0'});
      if (response.status === 200) {
        if (checked) {
          let items = response.data.map(item => {
            let obj = {}
            if (item.checked) obj = {...item}
            else obj = null
            return obj
          })
          var r = items.filter(s => {
            return s && s.trim()
          });
          this.groupTreeData = r
        } else {
          this.groupTreeData = response.data
        this.groupTreeData = response.data;  // 将获取到的数据赋值给集团分类树数据
        await this.filterCheckedNodes(this.groupTreeData, this.defaultCheckedKeys);
        await this.$nextTick(() => {
          this.$refs.groupTree.setCheckedKeys(this.defaultCheckedKeys);
        });
        this.handlerTreeData(this.groupTreeData);
        this.defaultCheckedKeys = [];
      }
    },
    // 过滤出来checked为true的节点
    filterCheckedNodes(data, checkedNodes) {
      data.forEach(node => {
        if (node.checked) {
          checkedNodes.push(node.oid);
          this.getGridAttrMapping(node.oid);
          this.form.groupValue = node.oid;
          this.groupVal = node.name;
        }
        if (node.children && node.children.length > 0) {
          this.filterCheckedNodes(node.children, checkedNodes);
        }
      });
    },
    // 过滤处理每个节点
    handlerTreeNode(node) {
      // 根据节点禁用状态设置节点是否禁用
      this.$set(node, 'disabled', !node.checked);
      if (node.children && node.children.length > 0) {
        // 递归循环处理所有子节点
        node.children.forEach(child => this.handlerTreeNode(child));
      }
    },
    // 检查所有节点是否都是未禁用状态
    allNodeChecked(data) {
      for (const node of data) {
        // 如果存在禁用节点返回false
        if (node.checked) {
          return false;
        }
        // 如果存在子节点且子节点存在禁用节点,则返回false
        if (node.children && !this.allNodeChecked(node.children)) {
          return false;
        }
      }
      // 没有禁用节点返回true
      return true;
    },
    // 集团分类树禁用数据处理整合方法
    handlerTreeData(data) {
      if (this.allNodeChecked(data)) {
        // 如果所有节点都未禁用将所有节点设置为false
        data.forEach(node => {
          this.$set(node, 'disabled', false); // Vue 3 中可能不需要这样做
        });
        return;
      }
      // 查找禁用节点
      let checkedNode = data.find(node => node.checked);
      data.forEach(node => {
        // 如果节点不是禁用节点设置为true
        this.$set(node, 'disabled', node !== checkedNode);
        if (node.children && node.children.length > 0) {
          // 调用循环节点
          this.handlerTreeNode(node);
        }
      });
    },
    // 左侧树过滤搜索
    filterNode(value, data) {
@@ -303,6 +536,17 @@
    },
    // 保存按钮
    async handleSave() {
      const getTargetCorresponding = (row) => {
        return this.targetColumn.dicData.find(column => column.targetAttrName === row.targetAttrName);
      }
      for (const item of this.mappingData) {
        item.$cellEdit = false;
        if (item.targetAttrName && !item.targetAttrId && !item.targetAttrKey) {
          const {targetAttrId, targetAttrKey, targetAttrName} = await getTargetCorresponding(item);
          Object.assign(item, {targetAttrId, targetAttrKey, targetAttrName});
        }
      }
      this.mappingData[this.selectRow].dockingPreAttrRangeVoList = (this.rangeData)
      const response = await batchAddSave({dockingPreAttrMappingVOList: this.mappingData})
      if (response.status === 200) {
        this.$message({
@@ -323,9 +567,13 @@
          message: "请选择一条集团分类"
        });
      } else {
        const response = await syncClassifyModel(param)
        if (response.status === 200) {
          console.log(response)
        try {
          const response = await syncClassifyModel(param);
          if (response.status === 200) {
            this.$message.success(response.data.msg)
          }
        } catch (error) {
          console.error(error);
        }
      }
    },
@@ -339,25 +587,26 @@
        });
      } else {
        const findRow = that.mappingData.findIndex(item => item.metaListId === that.mappingForm.metaListId)
        that.mappingData[findRow].targetAttrName = transferValue[0].name
        that.mappingData[findRow].targetAttrId = transferValue[0].oid
        that.mappingData[findRow].targetAttrName = transferValue[0].name;
        that.mappingData[findRow].targetAttrId = transferValue[0].oid;
        that.mappingData[findRow].targetAttrKey = transferValue[0].id;
        this.dialogPush = false;
      }
    },
    // 左侧树点击
    handelTreeCell(event) {
      if (event.leaf) {
        this.treeParam.codeClassifyId = event.oid
        this.form.groupValue = ''
        this.groupVal = ''
        this.tableData = []
        this.referTree(event.oid, event.checked)
        this.getListCodeByClassId(event.oid)
      }
      this.TreeOid = event.oid;
      this.treeParam.codeClassifyId = event.oid
      this.form.groupValue = ''
      this.groupVal = ''
      this.tableData = []
      this.mappingData = []
      this.referTree(event.oid)
      this.getListCodeByClassId(event.oid)
    },
    // 集团分类树点击
    handleNodeClick(data) {
    // 集团分类树选择
    handleCheck(data) {
      this.form.groupValue = data.oid
      this.groupVal = data.name
      this.$refs.selectTree.blur()
@@ -381,6 +630,9 @@
    },
    // 集团映射属性行选择(单击)
    handleMapingClick(row) {
      // console.log(row)
      this.tableSelectId = row.targetAttrKey || "";
      this.selectRow = row.$index;
      clearTimeout(this.times)
      this.mappingForm = row
      this.times = setTimeout(() => {
@@ -405,13 +657,42 @@
      done();
    },
    // 属性取值范围单元格编辑后
    handleMapingUpdate(row, index, done) {
      this.$message({
        showClose: true,
        message: "修改成功",
        type: "success",
      });
      done();
    async handleMapingUpdate(row, index, done) {
      const getTargetCorresponding = async (row) => {
        return this.targetColumn.dicData.find(column => column.targetAttrName === row.targetAttrName);
      };
      try {
        if (!row.targetAttrName) {
          this.$message.warning('请选择要保存的属性名称!');
          done();
          return;
        }
        // 获取目标属性信息
        const {targetAttrId, targetAttrKey, targetAttrName} = await getTargetCorresponding(row);
        // 更新行数据
        Object.assign(row, {
          targetAttrId: targetAttrId.toLowerCase(),
          targetAttrKey: targetAttrKey.toLowerCase(),
          targetAttrName: targetAttrName.toLowerCase()
        });
        const response = await batchAddSave({dockingPreAttrMappingVOList: [row]});
        if (response.status === 200) {
          this.$message({
            type: "success",
            message: "修改成功!"
          });
        }
      } catch (error) {
        this.$message.error(error)
      } finally {
        done();
      }
    },
    // 集团映射属性选择(单选)
    selectionChange(selection) {
@@ -425,37 +706,47 @@
      }
    },
    setCurrentRow(selection, row) {
      this.mappingForm = row
      this.disabledPush = false
    },
    handleSelectAll(selection) {
      this.$refs.crudMapping.toggleSelection()
    }
    },
  }
}
</script>
<style lang="scss" scoped>
  .setstyle {
    min-height: 200px;
    padding: 0 !important;
    margin: 0;
    overflow: auto;
    cursor: default !important;
.setstyle {
  min-height: 200px;
  padding: 0 !important;
  margin: 0;
  overflow: auto;
  cursor: default !important;
}
::v-deep {
  .el-transfer-panel__list {
    width: 100%;
    height: 370px;
  }
  ::v-deep{
    .el-transfer-panel__list {
      width: 100%;
      height: 370px;
    }
    .el-transfer-panel__body {
      height: 370px;
    }
    .el-input {
      width: auto;
    }
    .el-transfer-panel {
      width: 270px;
    }
  .el-transfer-panel__body {
    height: 370px;
  }
  .el-input {
    width: auto;
  }
  .el-transfer-panel {
    width: 270px;
  }
  .el-scrollbar__view {
    height: 100px;
  }
}
</style>