Quellcode durchsuchen

修改配置动参从点位取值 新增项目 建筑楼层 租户

haojianlong vor 5 Jahren
Ursprung
Commit
e409c74406

+ 281 - 0
src/components/point/dynamicdata/buildRules.vue

@@ -0,0 +1,281 @@
+
+<template>
+  <div id="equipRules">
+    <!-- 查询条件 -->
+    <div class="query-area">
+      <el-row>
+        <el-col :span="22">
+          <el-form ref="form" :model="form" :inline="true">
+            <el-row>
+              <div class="query-item special" style="margin-bottom:20px;">
+                <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryTableData">
+                  <i slot="suffix" class="el-input__icon el-icon-search" @click="queryTableData"></i>
+                </el-input>
+              </div>
+              <div class="query-item">
+                <label style="padding-right:9px">数据源</label>
+                <dataSource :Related="null" @change="changeDataSource" :typeName="typeName"></dataSource>
+              </div>
+              <!-- <div class="query-item">
+                <dictionary-device @change="changeDictionary" :Related="null" :typeName="typeName"></dictionary-device>
+              </div> -->
+              <div class="query-item">
+                <label style="padding-right:9px">位置标签</label>
+                <locationFlag :Related="null" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+              </div>
+            </el-row>
+          </el-form>
+        </el-col>
+        <el-col :span="2">
+          <el-tooltip class="item" effect="dark" :content="`填充${typeName}标识与实例对应规则`" placement="left">
+            <el-button size="small" type="primary" @click="toAddRelation" icon="el-icon-edit-outline" style="float:right;" :disabled="isPending">
+            </el-button>
+          </el-tooltip>
+          <el-tooltip class="item" effect="dark" :content="`根据${typeName}标识自动填充实例`" placement="left">
+            <el-button size="small" type="primary" @click="autoGroupRela" :icon="autoLoading?'iconfont icon-jiazai':'iconfont icon-zidong'"
+              style="float:right;padding:7px 14px;margin-right:10px;" :disabled="autoLoading"></el-button>
+          </el-tooltip>
+        </el-col>
+      </el-row>
+    </div>
+    <el-row>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">点位表中出现的所有{{typeName}}</el-col>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">对应物理世界中的{{typeName}}实例</el-col>
+    </el-row>
+    <!-- 列表区域 -->
+    <div class="table-area">
+      <el-table :data="tableData" style="width: 100%" height="100%" v-loading="loading" :header-cell-style="headerStyle">
+        <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip min-width="200"></el-table-column>
+        <el-table-column prop="LocationFlag" label="位置标签" min-width="200">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+              <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column prop="Datasource" label="数据源" show-overflow-tooltip min-width="100"></el-table-column>
+        <!-- <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip min-width="100"></el-table-column> -->
+        <el-table-column prop="ObjectLocalName" :label="`${typeName}实例`" show-overflow-tooltip min-width="400" class-name="td-bl"></el-table-column>
+        <!-- <el-table-column label="所在建筑楼层" min-width="300">
+          <template slot-scope="scope">
+            <el-tooltip
+              :content="scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''"
+              placement="top">
+              <div class="tool-tip">
+                {{scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''}}
+              </div>
+            </el-tooltip>
+          </template>
+        </el-table-column> -->
+        <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip min-width="100"></el-table-column>
+        <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip min-width="100"></el-table-column> -->
+        <el-table-column prop="action" label="操作" min-width="100">
+          <template slot-scope="scope">
+            <el-tooltip class="item" effect="dark" content="清除对应规则" placement="left">
+              <el-button size="mini" @click="handleDelete(scope.$index, scope.row)" type="danger" plain
+                :disabled="isPending||scope.row.Related=='False'" icon="el-icon-delete"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <!-- 分页 -->
+    <el-pagination class="fr" v-show="tableData && tableData.length" @size-change="handleSizeChange" @current-change="handleCurrentChange"
+      :current-page="page.pageNumber" :page-sizes="page.pageSizes" :page-size="page.pageSize" layout="total, sizes, prev, pager, next, jumper"
+      :total="page.total"></el-pagination>
+    <!-- 清除对应关系弹窗 -->
+    <del-relation-dialog ref="del" @refresh="refresh" :typeName="'space'"></del-relation-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import { mapGetters, mapActions } from "vuex";
+import applyRulesDialog from "@/components/point/dynamicdata/applyRulesDialog";
+import delRelationDialog from "@/components/point/dynamicdata/delRelationDialog";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import equipRules from "@/components/point/dynamicdata/equipRules";
+import {
+  dynamicClashConfirm,
+  dynamicExecute,
+  dynamicQuery,
+  dynamicQueryPoint,
+  dynamicPointTypeList
+} from "@/api/scan/request";
+export default {
+  computed: {
+    ...mapGetters("layout", ["projectId"])
+  },
+  data() {
+    return {
+      headerStyle: {
+        backgroundColor: '#e1e4e5',
+        color: '#2b2b2b',
+        lineHeight: '30px'
+      },
+      sourceList: [], //数据源
+      DynEquipList: [], //数据字典部件类型
+      LocFlagList: [], //位置标签
+      tableData: [], //列表数据
+      page: {
+        pageSize: 50,
+        pageSizes: [10, 20, 50, 100],
+        pageNumber: 1,
+        total: 0
+      },
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [] //数据源
+      }, //查询条件
+      loading: false, //列表loading
+      autoLoading: false, //自动对应实例
+    };
+  },
+  components: {
+    applyRulesDialog,
+    delRelationDialog,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  props: {
+    typeName: {},
+    isPending: {
+      default: false
+    }
+  },
+  created() {
+    this.init();
+  },
+  mounted() { },
+  methods: {
+    //获取查询条件-提示信息
+    init() {
+      this.getTableData()
+    },
+    //清除对应关系
+    handleDelete(index, row) {
+      this.$refs.del.showDialog(row);
+    },
+    //改变pagesize
+    handleSizeChange(pageSize) {
+      this.page.pageSize = pageSize;
+      this.getTableData();
+    },
+    //改变pageno
+    handleCurrentChange(pageNo) {
+      this.page.pageNumber = pageNo;
+      this.getTableData();
+    },
+    //跳转至填充对应规则
+    toAddRelation() {
+      this.$router.push({ path: "buildRela", query: { typeName: this.typeName } });
+    },
+    //获取表格数据
+    getTableData() {
+      let param = {
+        PageNumber: this.page.pageNumber,
+        PageSize: this.page.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.loading = true;
+      // 查询对应关系(P1)
+      dynamicQuery(param, res => {
+        this.loading = false;
+        this.tableData = res.Content;
+        this.page.total = res.PageSize < 50 ? res.PageSize : res.Total;
+      });
+    },
+    //刷新列表
+    refresh() {
+      this.page.pageNumber = 1;
+      this.$emit('refresh', this.typeName);
+      this.getTableData();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.page.pageNumber = 1;
+      this.form.SubTypeName = val;
+      this.getTableData();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.page.pageNumber = 1;
+      this.form.Datasource = val;
+      this.getTableData();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.page.pageNumber = 1;
+      this.form.LocationFlag = val;
+      this.getTableData();
+    },
+    //查询列表
+    queryTableData() {
+      this.page.pageNumber = 1;
+      this.getTableData();
+    },
+    //自动对应实例
+    autoGroupRela() {
+      this.$emit('autoGroupRela', 'space');
+    }
+  },
+  watch: {
+    projectId() {
+      this.init();
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+#equipRules {
+  height: calc(100% - 54px);
+  /deep/ .text-right {
+    text-align: right;
+  }
+  .query-item {
+    float: left;
+    & + .query-item {
+      margin-left: 10px;
+    }
+  }
+  /deep/ .special .el-input__inner {
+    width: 220px;
+  }
+  /deep/ .table-area {
+    width: 100%;
+    height: calc(100% - 71px);
+    margin-bottom: 10px;
+    td div {
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      overflow: hidden;
+    }
+    .td-bl {
+      border-left: 1px solid #ebeef5;
+    }
+    .tool-tip {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  }
+}
+</style>
+

+ 16 - 1
src/components/point/dynamicdata/delRelationDialog.vue

@@ -110,7 +110,22 @@ export default {
           { label: '对象实例名称', prop: "ObjectLocalName" },
           { label: '对象本地编码', prop: "ObjectLocalCode" },
           { label: '所在建筑楼层', prop: "BuildLocalName" },
-        ]
+        ],
+        build: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象本地编码', prop: "ObjectLocalCode" }
+        ],
+        floor: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象顺序号', prop: "ObjectLocalCode" },
+          { label: '所在建筑', prop: "BuildLocalName" },
+        ],
+        tenant: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象本地编码', prop: "ObjectLocalCode" },
+          { label: '所在业务空间', prop: "BuildLocalName" },
+        ],
+        project: [],
       },
       rightTableHeader: []
     };

+ 279 - 0
src/components/point/dynamicdata/floorRules.vue

@@ -0,0 +1,279 @@
+
+<template>
+  <div id="equipRules">
+    <!-- 查询条件 -->
+    <div class="query-area">
+      <el-row>
+        <el-col :span="22">
+          <el-form ref="form" :model="form" :inline="true">
+            <el-row>
+              <div class="query-item special" style="margin-bottom:20px;">
+                <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryTableData">
+                  <i slot="suffix" class="el-input__icon el-icon-search" @click="queryTableData"></i>
+                </el-input>
+              </div>
+              <div class="query-item">
+                <label style="padding-right:9px">数据源</label>
+                <dataSource :Related="null" @change="changeDataSource" :typeName="typeName"></dataSource>
+              </div>
+              <!-- <div class="query-item">
+                <dictionary-device @change="changeDictionary" :Related="null" :typeName="typeName"></dictionary-device>
+              </div> -->
+              <div class="query-item">
+                <label style="padding-right:9px">位置标签</label>
+                <locationFlag :Related="null" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+              </div>
+            </el-row>
+          </el-form>
+        </el-col>
+        <el-col :span="2">
+          <el-tooltip class="item" effect="dark" :content="`填充${typeName}标识与实例对应规则`" placement="left">
+            <el-button size="small" type="primary" @click="toAddRelation" icon="el-icon-edit-outline" style="float:right;" :disabled="isPending"></el-button>
+          </el-tooltip>
+          <el-tooltip class="item" effect="dark" :content="`根据${typeName}标识自动填充实例`" placement="left">
+            <el-button size="small" type="primary" @click="autoGroupRela" :icon="autoLoading?'iconfont icon-jiazai':'iconfont icon-zidong'"
+              style="float:right;padding:7px 14px;margin-right:10px;" :disabled="autoLoading"></el-button>
+          </el-tooltip>
+        </el-col>
+      </el-row>
+    </div>
+    <el-row>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">点位表中出现的所有{{typeName}}</el-col>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">对应物理世界中的{{typeName}}实例</el-col>
+    </el-row>
+    <!-- 列表区域 -->
+    <div class="table-area">
+      <el-table :data="tableData" style="width: 100%" height="100%" v-loading="loading" :header-cell-style="headerStyle">
+        <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip min-width="200"></el-table-column>
+        <el-table-column prop="LocationFlag" label="位置标签" min-width="200">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+              <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column prop="Datasource" label="数据源" show-overflow-tooltip min-width="100"></el-table-column>
+        <!-- <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip min-width="100"></el-table-column> -->
+        <el-table-column prop="ObjectLocalName" :label="`${typeName}实例`" show-overflow-tooltip min-width="100" class-name="td-bl"></el-table-column>
+        <el-table-column prop="ObjectLocalName" :label="`${typeName}顺序号`" show-overflow-tooltip min-width="100" class-name="td-bl"></el-table-column>
+        <el-table-column label="所属建筑" min-width="200">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''"
+              placement="top">
+              <div class="tool-tip">{{scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''}}
+              </div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip min-width="100"></el-table-column>
+        <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip min-width="100"></el-table-column> -->
+        <el-table-column prop="action" label="操作" min-width="100">
+          <template slot-scope="scope">
+            <el-tooltip class="item" effect="dark" content="清除对应规则" placement="left">
+              <el-button size="mini" @click="handleDelete(scope.$index, scope.row)" type="danger" plain :disabled="isPending||scope.row.Related=='False'"
+                icon="el-icon-delete"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <!-- 分页 -->
+    <el-pagination class="fr" v-show="tableData && tableData.length" @size-change="handleSizeChange" @current-change="handleCurrentChange"
+      :current-page="page.pageNumber" :page-sizes="page.pageSizes" :page-size="page.pageSize" layout="total, sizes, prev, pager, next, jumper"
+      :total="page.total"></el-pagination>
+    <!-- 清除对应关系弹窗 -->
+    <del-relation-dialog ref="del" @refresh="refresh" :typeName="'space'"></del-relation-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import { mapGetters, mapActions } from "vuex";
+import applyRulesDialog from "@/components/point/dynamicdata/applyRulesDialog";
+import delRelationDialog from "@/components/point/dynamicdata/delRelationDialog";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import equipRules from "@/components/point/dynamicdata/equipRules";
+import {
+  dynamicClashConfirm,
+  dynamicExecute,
+  dynamicQuery,
+  dynamicQueryPoint,
+  dynamicPointTypeList
+} from "@/api/scan/request";
+export default {
+  computed: {
+    ...mapGetters("layout", ["projectId"])
+  },
+  data() {
+    return {
+      headerStyle: {
+        backgroundColor: '#e1e4e5',
+        color: '#2b2b2b',
+        lineHeight: '30px'
+      },
+      sourceList: [], //数据源
+      DynEquipList: [], //数据字典部件类型
+      LocFlagList: [], //位置标签
+      tableData: [], //列表数据
+      page: {
+        pageSize: 50,
+        pageSizes: [10, 20, 50, 100],
+        pageNumber: 1,
+        total: 0
+      },
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [] //数据源
+      }, //查询条件
+      loading: false, //列表loading
+      autoLoading: false, //自动对应实例
+    };
+  },
+  components: {
+    applyRulesDialog,
+    delRelationDialog,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  props: {
+    typeName: {},
+    isPending: {
+      default: false
+    }
+  },
+  created() {
+    this.init();
+  },
+  mounted() { },
+  methods: {
+    //获取查询条件-提示信息
+    init() {
+      this.getTableData()
+    },
+    //清除对应关系
+    handleDelete(index, row) {
+      this.$refs.del.showDialog(row);
+    },
+    //改变pagesize
+    handleSizeChange(pageSize) {
+      this.page.pageSize = pageSize;
+      this.getTableData();
+    },
+    //改变pageno
+    handleCurrentChange(pageNo) {
+      this.page.pageNumber = pageNo;
+      this.getTableData();
+    },
+    //跳转至填充对应规则
+    toAddRelation() {
+      this.$router.push({ path: "floorRela", query: { typeName: this.typeName } });
+    },
+    //获取表格数据
+    getTableData() {
+      let param = {
+        PageNumber: this.page.pageNumber,
+        PageSize: this.page.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.loading = true;
+      // 查询对应关系(P1)
+      dynamicQuery(param, res => {
+        this.loading = false;
+        this.tableData = res.Content;
+        this.page.total = res.PageSize < 50 ? res.PageSize : res.Total;
+      });
+    },
+    //刷新列表
+    refresh() {
+      this.page.pageNumber = 1;
+      this.$emit('refresh', this.typeName);
+      this.getTableData();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.page.pageNumber = 1;
+      this.form.SubTypeName = val;
+      this.getTableData();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.page.pageNumber = 1;
+      this.form.Datasource = val;
+      this.getTableData();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.page.pageNumber = 1;
+      this.form.LocationFlag = val;
+      this.getTableData();
+    },
+    //查询列表
+    queryTableData() {
+      this.page.pageNumber = 1;
+      this.getTableData();
+    },
+    //自动对应实例
+    autoGroupRela() {
+      this.$emit('autoGroupRela','space');
+    }
+  },
+  watch: {
+    projectId() {
+      this.init();
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+#equipRules {
+  height: calc(100% - 54px);
+  /deep/ .text-right {
+    text-align: right;
+  }
+  .query-item {
+    float: left;
+    & + .query-item {
+      margin-left: 10px;
+    }
+  }
+  /deep/ .special .el-input__inner {
+    width: 220px;
+  }
+  /deep/ .table-area {
+    width: 100%;
+    height: calc(100% - 71px);
+    margin-bottom: 10px;
+    td div {
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      overflow: hidden;
+    }
+    .td-bl {
+      border-left: 1px solid #ebeef5;
+    }
+    .tool-tip {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  }
+}
+</style>
+

+ 22 - 5
src/components/point/dynamicdata/historyDialog.vue

@@ -7,7 +7,8 @@
           <el-table-column v-for="item in showLeftHeader" :label="item.label" align="right" :key="item.prop">
             <template slot-scope="scope">
               <div v-for="t in scope.row.leftList" :key="t.id">
-                <el-tooltip :content="t[item.prop]&&t[item.prop].length?t[item.prop].toString():''" placement="top" :disabled="!(t[item.prop]&&t[item.prop].length)">
+                <el-tooltip :content="t[item.prop]&&t[item.prop].length?t[item.prop].toString():''" placement="top"
+                  :disabled="!(t[item.prop]&&t[item.prop].length)">
                   <div>{{t[item.prop]&&t[item.prop].length?t[item.prop].toString():'--'}}</div>
                 </el-tooltip>
               </div>
@@ -21,7 +22,8 @@
                 <div v-if="item.prop=='BuildLocalName'">
                   <el-tooltip
                     :content="t.BuildLocalName&&t.BuildLocalName!='null'?t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:t.BuildLocalName:''"
-                    placement="top" :disabled="!(t.BuildLocalName&&t.BuildLocalName!='null'?t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:t.BuildLocalName:'')">
+                    placement="top"
+                    :disabled="!(t.BuildLocalName&&t.BuildLocalName!='null'?t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:t.BuildLocalName:'')">
                     <div>
                       {{t.BuildLocalName&&t.BuildLocalName!='null'?t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:t.BuildLocalName:'--'}}
                     </div>
@@ -62,7 +64,7 @@ export default {
       showLeftHeader: [
         { label: '数据源', prop: 'Datasource' },
         { label: '位置标签', prop: 'LocationFlag' },
-        { label: '设备标识', prop: 'EquipmentMark' }
+        { label: '对象标识', prop: 'EquipmentMark' }
       ],
       tableHeader: {
         equip: [
@@ -82,11 +84,26 @@ export default {
           { label: '对象实例名称', prop: 'ObjectLocalName' },
           { label: '对象本地编码', prop: 'ObjectLocalCode' }
         ],
-        space:[
+        space: [
           { label: '对象实例名称', prop: "ObjectLocalName" },
           { label: '对象本地编码', prop: "ObjectLocalCode" },
           { label: '所在建筑楼层', prop: "BuildLocalName" },
-        ]
+        ],
+        build: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象本地编码', prop: "ObjectLocalCode" }
+        ],
+        floor: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象顺序号', prop: "ObjectLocalCode" },
+          { label: '所在建筑', prop: "BuildLocalName" },
+        ],
+        tenant: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象本地编码', prop: "ObjectLocalCode" },
+          { label: '所在业务空间', prop: "BuildLocalName" },
+        ],
+        project: [],
       }, //所有类型列表头部
       showRightHeader: [], //实际显示的列表头部
     };

+ 8 - 2
src/components/point/dynamicdata/locationCascader.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-cascader placeholder="全部" :options="LocationList" v-model="value" filterable size="small" @change="changeCascader" change-on-select
+  <el-cascader placeholder="全部" :options="LocationList" v-model="value" filterable size="small" @change="changeCascader" :change-on-select="!isFloor"
     :props="props" clearable class="location"></el-cascader>
 </template>
 <script>
@@ -23,7 +23,10 @@ export default {
   },
   props: {
     Related: {},
-    typeName: {}
+    typeName: {},
+    isFloor: {
+      default: false
+    }
   },
   computed: {
     ...mapGetters("layout", ["projectId"])
@@ -36,6 +39,9 @@ export default {
         //添加未明确对象
         this.LocationList = this.delNullItems(this.LocationList)
         this.LocationList.map(item => {
+          if (this.isFloor) {
+            delete item.List;
+          }
           if (item.List) {
             item.List.map(child => {
               if (child.List) {

+ 278 - 0
src/components/point/dynamicdata/projectRules.vue

@@ -0,0 +1,278 @@
+
+<template>
+  <div id="equipRules">
+    <!-- 查询条件 -->
+    <div class="query-area">
+      <el-row>
+        <el-col :span="22">
+          <el-form ref="form" :model="form" :inline="true">
+            <el-row>
+              <div class="query-item special" style="margin-bottom:20px;">
+                <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryTableData">
+                  <i slot="suffix" class="el-input__icon el-icon-search" @click="queryTableData"></i>
+                </el-input>
+              </div>
+              <div class="query-item">
+                <label style="padding-right:9px">数据源</label>
+                <dataSource :Related="null" @change="changeDataSource" :typeName="typeName"></dataSource>
+              </div>
+              <!-- <div class="query-item">
+                <dictionary-device @change="changeDictionary" :Related="null" :typeName="typeName"></dictionary-device>
+              </div> -->
+              <div class="query-item">
+                <label style="padding-right:9px">位置标签</label>
+                <locationFlag :Related="null" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+              </div>
+            </el-row>
+          </el-form>
+        </el-col>
+        <!-- <el-col :span="2">
+          <el-tooltip class="item" effect="dark" :content="`填充${typeName}标识与实例对应规则`" placement="left">
+            <el-button size="small" type="primary" @click="toAddRelation" icon="el-icon-edit-outline" style="float:right;" :disabled="isPending"></el-button>
+          </el-tooltip>
+          <el-tooltip class="item" effect="dark" :content="`根据${typeName}标识自动填充实例`" placement="left">
+            <el-button size="small" type="primary" @click="autoGroupRela" :icon="autoLoading?'iconfont icon-jiazai':'iconfont icon-zidong'"
+              style="float:right;padding:7px 14px;margin-right:10px;" :disabled="autoLoading"></el-button>
+          </el-tooltip>
+        </el-col> -->
+      </el-row>
+    </div>
+    <el-row>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">点位表中出现的所有{{typeName}}</el-col>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">对应物理世界中的{{typeName}}实例</el-col>
+    </el-row>
+    <!-- 列表区域 -->
+    <div class="table-area">
+      <el-table :data="tableData" style="width: 100%" height="100%" v-loading="loading" :header-cell-style="headerStyle">
+        <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip min-width="100"></el-table-column>
+        <el-table-column prop="LocationFlag" label="位置标签" min-width="200">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+              <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column prop="Datasource" label="数据源" show-overflow-tooltip min-width="100"></el-table-column>
+        <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip min-width="100"></el-table-column>
+        <el-table-column prop="ObjectLocalName" :label="`${typeName}实例`" show-overflow-tooltip min-width="100" class-name="td-bl"></el-table-column>
+        <el-table-column label="所在建筑楼层" min-width="300">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''"
+              placement="top">
+              <div class="tool-tip">{{scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''}}
+              </div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip min-width="100"></el-table-column>
+        <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip min-width="100"></el-table-column> -->
+        <el-table-column prop="action" label="操作" min-width="100">
+          <template slot-scope="scope">
+            <el-tooltip class="item" effect="dark" content="清除对应规则" placement="left">
+              <el-button size="mini" @click="handleDelete(scope.$index, scope.row)" type="danger" plain :disabled="isPending||scope.row.Related=='False'"
+                icon="el-icon-delete"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <!-- 分页 -->
+    <el-pagination class="fr" v-show="tableData && tableData.length" @size-change="handleSizeChange" @current-change="handleCurrentChange"
+      :current-page="page.pageNumber" :page-sizes="page.pageSizes" :page-size="page.pageSize" layout="total, sizes, prev, pager, next, jumper"
+      :total="page.total"></el-pagination>
+    <!-- 清除对应关系弹窗 -->
+    <del-relation-dialog ref="del" @refresh="refresh" :typeName="'space'"></del-relation-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import { mapGetters, mapActions } from "vuex";
+import applyRulesDialog from "@/components/point/dynamicdata/applyRulesDialog";
+import delRelationDialog from "@/components/point/dynamicdata/delRelationDialog";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import equipRules from "@/components/point/dynamicdata/equipRules";
+import {
+  dynamicClashConfirm,
+  dynamicExecute,
+  dynamicQuery,
+  dynamicQueryPoint,
+  dynamicPointTypeList
+} from "@/api/scan/request";
+export default {
+  computed: {
+    ...mapGetters("layout", ["projectId"])
+  },
+  data() {
+    return {
+      headerStyle: {
+        backgroundColor: '#e1e4e5',
+        color: '#2b2b2b',
+        lineHeight: '30px'
+      },
+      sourceList: [], //数据源
+      DynEquipList: [], //数据字典部件类型
+      LocFlagList: [], //位置标签
+      tableData: [], //列表数据
+      page: {
+        pageSize: 50,
+        pageSizes: [10, 20, 50, 100],
+        pageNumber: 1,
+        total: 0
+      },
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [] //数据源
+      }, //查询条件
+      loading: false, //列表loading
+      autoLoading: false, //自动对应实例
+    };
+  },
+  components: {
+    applyRulesDialog,
+    delRelationDialog,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  props: {
+    typeName: {},
+    isPending: {
+      default: false
+    }
+  },
+  created() {
+    this.init();
+  },
+  mounted() { },
+  methods: {
+    //获取查询条件-提示信息
+    init() {
+      this.getTableData()
+    },
+    //清除对应关系
+    handleDelete(index, row) {
+      this.$refs.del.showDialog(row);
+    },
+    //改变pagesize
+    handleSizeChange(pageSize) {
+      this.page.pageSize = pageSize;
+      this.getTableData();
+    },
+    //改变pageno
+    handleCurrentChange(pageNo) {
+      this.page.pageNumber = pageNo;
+      this.getTableData();
+    },
+    //跳转至填充对应规则
+    toAddRelation() {
+      this.$router.push({ path: "spaceRela", query: { typeName: this.typeName } });
+    },
+    //获取表格数据
+    getTableData() {
+      let param = {
+        PageNumber: this.page.pageNumber,
+        PageSize: this.page.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.loading = true;
+      // 查询对应关系(P1)
+      dynamicQuery(param, res => {
+        this.loading = false;
+        this.tableData = res.Content;
+        this.page.total = res.PageSize < 50 ? res.PageSize : res.Total;
+      });
+    },
+    //刷新列表
+    refresh() {
+      this.page.pageNumber = 1;
+      this.$emit('refresh', this.typeName);
+      this.getTableData();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.page.pageNumber = 1;
+      this.form.SubTypeName = val;
+      this.getTableData();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.page.pageNumber = 1;
+      this.form.Datasource = val;
+      this.getTableData();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.page.pageNumber = 1;
+      this.form.LocationFlag = val;
+      this.getTableData();
+    },
+    //查询列表
+    queryTableData() {
+      this.page.pageNumber = 1;
+      this.getTableData();
+    },
+    //自动对应实例
+    autoGroupRela() {
+      this.$emit('autoGroupRela','space');
+    }
+  },
+  watch: {
+    projectId() {
+      this.init();
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+#equipRules {
+  height: calc(100% - 54px);
+  /deep/ .text-right {
+    text-align: right;
+  }
+  .query-item {
+    float: left;
+    & + .query-item {
+      margin-left: 10px;
+    }
+  }
+  /deep/ .special .el-input__inner {
+    width: 220px;
+  }
+  /deep/ .table-area {
+    width: 100%;
+    height: calc(100% - 71px);
+    margin-bottom: 10px;
+    td div {
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      overflow: hidden;
+    }
+    .td-bl {
+      border-left: 1px solid #ebeef5;
+    }
+    .tool-tip {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  }
+}
+</style>
+

+ 278 - 0
src/components/point/dynamicdata/tenantRules.vue

@@ -0,0 +1,278 @@
+
+<template>
+  <div id="equipRules">
+    <!-- 查询条件 -->
+    <div class="query-area">
+      <el-row>
+        <el-col :span="22">
+          <el-form ref="form" :model="form" :inline="true">
+            <el-row>
+              <div class="query-item special" style="margin-bottom:20px;">
+                <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryTableData">
+                  <i slot="suffix" class="el-input__icon el-icon-search" @click="queryTableData"></i>
+                </el-input>
+              </div>
+              <div class="query-item">
+                <label style="padding-right:9px">数据源</label>
+                <dataSource :Related="null" @change="changeDataSource" :typeName="typeName"></dataSource>
+              </div>
+              <!-- <div class="query-item">
+                <dictionary-device @change="changeDictionary" :Related="null" :typeName="typeName"></dictionary-device>
+              </div> -->
+              <div class="query-item">
+                <label style="padding-right:9px">位置标签</label>
+                <locationFlag :Related="null" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+              </div>
+            </el-row>
+          </el-form>
+        </el-col>
+        <el-col :span="2">
+          <el-tooltip class="item" effect="dark" :content="`填充${typeName}标识与实例对应规则`" placement="left">
+            <el-button size="small" type="primary" @click="toAddRelation" icon="el-icon-edit-outline" style="float:right;" :disabled="isPending"></el-button>
+          </el-tooltip>
+          <el-tooltip class="item" effect="dark" :content="`根据${typeName}标识自动填充实例`" placement="left">
+            <el-button size="small" type="primary" @click="autoGroupRela" :icon="autoLoading?'iconfont icon-jiazai':'iconfont icon-zidong'"
+              style="float:right;padding:7px 14px;margin-right:10px;" :disabled="autoLoading"></el-button>
+          </el-tooltip>
+        </el-col>
+      </el-row>
+    </div>
+    <el-row>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">点位表中出现的所有{{typeName}}</el-col>
+      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">对应物理世界中的{{typeName}}实例</el-col>
+    </el-row>
+    <!-- 列表区域 -->
+    <div class="table-area">
+      <el-table :data="tableData" style="width: 100%" height="100%" v-loading="loading" :header-cell-style="headerStyle">
+        <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip min-width="200"></el-table-column>
+        <el-table-column prop="LocationFlag" label="位置标签" min-width="200">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+              <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <el-table-column prop="Datasource" label="数据源" show-overflow-tooltip min-width="100"></el-table-column>
+        <!-- <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip min-width="100"></el-table-column> -->
+        <el-table-column prop="ObjectLocalName" :label="`${typeName}实例`" show-overflow-tooltip min-width="100" class-name="td-bl"></el-table-column>
+        <el-table-column label="所在业务空间" min-width="300">
+          <template slot-scope="scope">
+            <el-tooltip :content="scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''"
+              placement="top">
+              <div class="tool-tip">{{scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''}}
+              </div>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+        <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip min-width="100"></el-table-column>
+        <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip min-width="100"></el-table-column> -->
+        <el-table-column prop="action" label="操作" min-width="100">
+          <template slot-scope="scope">
+            <el-tooltip class="item" effect="dark" content="清除对应规则" placement="left">
+              <el-button size="mini" @click="handleDelete(scope.$index, scope.row)" type="danger" plain :disabled="isPending||scope.row.Related=='False'"
+                icon="el-icon-delete"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <!-- 分页 -->
+    <el-pagination class="fr" v-show="tableData && tableData.length" @size-change="handleSizeChange" @current-change="handleCurrentChange"
+      :current-page="page.pageNumber" :page-sizes="page.pageSizes" :page-size="page.pageSize" layout="total, sizes, prev, pager, next, jumper"
+      :total="page.total"></el-pagination>
+    <!-- 清除对应关系弹窗 -->
+    <del-relation-dialog ref="del" @refresh="refresh" :typeName="'space'"></del-relation-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import { mapGetters, mapActions } from "vuex";
+import applyRulesDialog from "@/components/point/dynamicdata/applyRulesDialog";
+import delRelationDialog from "@/components/point/dynamicdata/delRelationDialog";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import equipRules from "@/components/point/dynamicdata/equipRules";
+import {
+  dynamicClashConfirm,
+  dynamicExecute,
+  dynamicQuery,
+  dynamicQueryPoint,
+  dynamicPointTypeList
+} from "@/api/scan/request";
+export default {
+  computed: {
+    ...mapGetters("layout", ["projectId"])
+  },
+  data() {
+    return {
+      headerStyle: {
+        backgroundColor: '#e1e4e5',
+        color: '#2b2b2b',
+        lineHeight: '30px'
+      },
+      sourceList: [], //数据源
+      DynEquipList: [], //数据字典部件类型
+      LocFlagList: [], //位置标签
+      tableData: [], //列表数据
+      page: {
+        pageSize: 50,
+        pageSizes: [10, 20, 50, 100],
+        pageNumber: 1,
+        total: 0
+      },
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [] //数据源
+      }, //查询条件
+      loading: false, //列表loading
+      autoLoading: false, //自动对应实例
+    };
+  },
+  components: {
+    applyRulesDialog,
+    delRelationDialog,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  props: {
+    typeName: {},
+    isPending: {
+      default: false
+    }
+  },
+  created() {
+    this.init();
+  },
+  mounted() { },
+  methods: {
+    //获取查询条件-提示信息
+    init() {
+      this.getTableData()
+    },
+    //清除对应关系
+    handleDelete(index, row) {
+      this.$refs.del.showDialog(row);
+    },
+    //改变pagesize
+    handleSizeChange(pageSize) {
+      this.page.pageSize = pageSize;
+      this.getTableData();
+    },
+    //改变pageno
+    handleCurrentChange(pageNo) {
+      this.page.pageNumber = pageNo;
+      this.getTableData();
+    },
+    //跳转至填充对应规则
+    toAddRelation() {
+      this.$router.push({ path: "tenantRela", query: { typeName: this.typeName } });
+    },
+    //获取表格数据
+    getTableData() {
+      let param = {
+        PageNumber: this.page.pageNumber,
+        PageSize: this.page.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.loading = true;
+      // 查询对应关系(P1)
+      dynamicQuery(param, res => {
+        this.loading = false;
+        this.tableData = res.Content;
+        this.page.total = res.PageSize < 50 ? res.PageSize : res.Total;
+      });
+    },
+    //刷新列表
+    refresh() {
+      this.page.pageNumber = 1;
+      this.$emit('refresh', this.typeName);
+      this.getTableData();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.page.pageNumber = 1;
+      this.form.SubTypeName = val;
+      this.getTableData();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.page.pageNumber = 1;
+      this.form.Datasource = val;
+      this.getTableData();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.page.pageNumber = 1;
+      this.form.LocationFlag = val;
+      this.getTableData();
+    },
+    //查询列表
+    queryTableData() {
+      this.page.pageNumber = 1;
+      this.getTableData();
+    },
+    //自动对应实例
+    autoGroupRela() {
+      this.$emit('autoGroupRela','space');
+    }
+  },
+  watch: {
+    projectId() {
+      this.init();
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+#equipRules {
+  height: calc(100% - 54px);
+  /deep/ .text-right {
+    text-align: right;
+  }
+  .query-item {
+    float: left;
+    & + .query-item {
+      margin-left: 10px;
+    }
+  }
+  /deep/ .special .el-input__inner {
+    width: 220px;
+  }
+  /deep/ .table-area {
+    width: 100%;
+    height: calc(100% - 71px);
+    margin-bottom: 10px;
+    td div {
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      overflow: hidden;
+    }
+    .td-bl {
+      border-left: 1px solid #ebeef5;
+    }
+    .tool-tip {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  }
+}
+</style>
+

+ 21 - 0
src/router/system.js

@@ -33,6 +33,9 @@ import equipRela from '@/views/point/dynamicdata/addRelation/equipRela'
 import partsRela from '@/views/point/dynamicdata/addRelation/partsRela'
 import systemRela from '@/views/point/dynamicdata/addRelation/systemRela'
 import spaceRela from '@/views/point/dynamicdata/addRelation/spaceRela'
+import buildRela from '@/views/point/dynamicdata/addRelation/buildRela'
+import floorRela from '@/views/point/dynamicdata/addRelation/floorRela'
+import tenantRela from '@/views/point/dynamicdata/addRelation/tenantRela'
 
 /** 扫楼作业 */
 import buildTask from '@/views/data_admin/buildTask'
@@ -181,6 +184,24 @@ export default [
                 meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识' }] }
             },
             {
+                path: 'buildRela',
+                name: 'buildRela',
+                component: buildRela,
+                meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识' }] }
+            },
+            {
+                path: 'floorRela',
+                name: 'floorRela',
+                component: floorRela,
+                meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识' }] }
+            },
+            {
+                path: 'tenantRela',
+                name: 'tenantRela',
+                component: tenantRela,
+                meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识' }] }
+            },
+            {
                 path: 'objectdata',
                 name: 'objectData',
                 component: objectData,

+ 174 - 0
src/views/point/dynamicdata/addRelation/addRelationCommon.scss

@@ -0,0 +1,174 @@
+
+#addRelation {
+  height: 100%;
+  background-color: #fff;
+  padding: 10px 20px 10px 10px;
+  .text-right {
+    text-align: right;
+  }
+  /deep/ .table-container {
+    height: calc(100% - 160px);
+    & > div {
+      width: 49.5%;
+      float: left;
+    }
+    .left-table,
+    .right-table {
+      height: 100%;
+      h5 {
+        line-height: 44px;
+        padding-left: 5px;
+        background-color: #e1e4e5;
+        color: #2b2b2b
+      }
+      .query-box {
+        margin-bottom: 20px;
+        .query-item {
+          float: left;
+          width: 30%;
+          margin: 5px;
+          label {
+            display: block;
+            margin-bottom: 5px;
+          }
+          .el-select {
+            width: 100%;
+          }
+        }
+      }
+      .table-box {
+        height: calc(100% - 50px);
+        position: relative;
+        .AIRecom {
+          position: absolute;
+          width: 96%;
+          height: 160px;
+          bottom: 5px;
+          left: 1%;
+          background-color: rgba(255, 255, 255, 0.9);
+          z-index: 3;
+          font-size: 12px;
+          .r-title {
+            padding-left: 10px;
+            i {
+              float: right;
+            }
+          }
+          .l-title {
+            text-align: right;
+            padding-right: 10px;
+            i {
+              float: left;
+            }
+          }
+          .ai-recom-item {
+            width: 30%;
+            height: 126px;
+            float: left;
+            border: 1px solid #ebeef5;
+            overflow: hidden;
+            padding-top: 5px;
+            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+            p {
+              position: relative;
+              white-space: nowrap;
+              text-overflow: ellipsis;
+              overflow: hidden;
+              padding: 0 8px;
+              z-index: -1;
+              span {
+                float: right;
+              }
+              .el-checkbox {
+                margin-right: 0;
+              }
+            }
+          }
+          .ai-recom-item.r-ai-recom-item + .ai-recom-item {
+            margin-left: 3%;
+          }
+          .l-ai-recom-item {
+            float: right;
+            p {
+              text-align: right;
+              padding-right: 10px;
+              span {
+                float: left;
+              }
+            }
+          }
+          .ai-recom-item.l-ai-recom-item + .ai-recom-item {
+            margin-right: 3%;
+          }
+        }
+      }
+      ::-webkit-scrollbar {
+        display: none;
+      }
+      //列表
+      .custom-table {
+        height: 100%;
+        .bgf5 {
+          background-color: #f5f7fa;
+        }
+        thead {
+          color: #2b2b2b;
+          tr th{
+            border-top:1px solid #EBEEF5;
+          }
+        }
+        thead label.el-checkbox {
+          display: none;
+        }
+      }
+      .l-custom-table {
+        td {
+          text-align: right;
+        }
+      }
+      .tool-tip {
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+  }
+  .fr {
+    padding-top: 10px;
+    float: right;
+  }
+  .clearfix:before,
+  .clearfix:after {
+    content: "";
+    display: block;
+    clear: both;
+  }
+  .clearfix {
+    zoom: 1;
+  }
+  .ani-his-plus {
+    position: relative;
+    .plusOne {
+      position: absolute;
+      color: red;
+      left: 50%;
+      bottom: 50%;
+      font-size: 20px;
+      font-weight: 800;
+    }
+    .startAni {
+      animation: fadeToTop 1s;
+    }
+  }
+  @keyframes fadeToTop {
+    0% {
+      left: 50%;
+      bottom: 50%;
+      opacity: 1;
+    }
+    100% {
+      bottom: 100%;
+      opacity: 0;
+    }
+  }
+}

+ 557 - 0
src/views/point/dynamicdata/addRelation/buildRela/index.vue

@@ -0,0 +1,557 @@
+<template>
+  <div id="addRelation">
+    <!-- 数据字典对象类型 -->
+    <el-row style="margin-bottom:16px;">
+      <el-col :span="12">
+        <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
+        <!-- <div style="display:inline-block;width:345px;margin-left:10px;">
+          <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
+        </div> -->
+      </el-col>
+      <el-col :span="12" class="text-right">
+        <el-switch @change="changeType" v-model="isSwitch"></el-switch>
+        <span style="padding:0 5px;">AI辅助</span>
+        <el-button size="medium" @click="showHistory" class="ani-his-plus">
+          本次对应记录 {{num}}
+          <span v-if="showPlus" :class="{'plusOne':true,'startAni':showPlus}">+1</span>
+        </el-button>
+      </el-col>
+    </el-row>
+    <!-- 列表及查询条件区域 -->
+    <div class="table-container clearfix">
+      <div class="left-table">
+        <h5>未对应的{{typeName}}标识</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}标识关键字</label>
+            <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryLeftTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryLeftTable"></i>
+            </el-input>
+          </div>
+          <div class="query-item">
+            <label>数据源</label>
+            <dataSource :Related="false" @change="changeDataSource" :typeName="typeName"></dataSource>
+          </div>
+          <div class="query-item">
+            <label>位置标签</label>
+            <locationFlag :Related="false" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+          </div>
+        </div>
+        <!-- 左侧列表 -->
+        <div class="table-box">
+          <div class="l-custom-table custom-table" v-loading="lTableLoading">
+            <el-table ref="lTableBody" :data="LtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickLeftRow"
+              @selection-change="leftSelectionChange">
+              <el-table-column label="数据源" align="right" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.Datasource }}</template>
+              </el-table-column>
+              <el-table-column label="位置标签" align="right">
+                <template slot-scope="scope">
+                  <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+                    <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!-- <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column> -->
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="leftRecoList.length&&leftAI&&rArray.length">
+              <div class="l-title">
+                可能对应的{{typeName}}标识
+                <i class="el-icon-close" @click="leftRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in leftRecoList" :key="t.id" class="l-ai-recom-item ai-recom-item" @click="selectAI(i,'l')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.EquipmentMark&&t.EquipmentMark!='null'?t.EquipmentMark:''}}</p>
+                <p>{{t.LocationFlag.toString()}}</p>
+                <!-- <p>{{t.address}}</p> -->
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+      <div class="right-table" style="float: right;">
+        <h5>未对应的{{typeName}}实例</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}实例关键字</label>
+            <el-input :placeholder="`请输入${typeName}实例关键字`" v-model="form.ObjectLocalName" @keyup.enter.native="queryRightTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryRightTable"></i>
+            </el-input>
+          </div>
+          <!-- <div class="query-item">
+            <label>所在位置</label>
+            <location-cas @change="changeLocation" :Related="false" :typeName="typeName"></location-cas>
+          </div> -->
+        </div>
+        <!-- 右侧列表 -->
+        <div class="table-box">
+          <div class="r-custom-table custom-table" v-loading="rTableLoading">
+            <el-table ref="rTableBody" :data="RtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickRightRow"
+              @selection-change="rightSelectionChange">
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+              <el-table-column :label="`${typeName}实例名称`" width="200" class-name="bgf5" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.ObjectLocalName }}</template>
+              </el-table-column>
+              <el-table-column prop="ObjectLocalCode" :label="`${typeName}本地编码`" show-overflow-tooltip></el-table-column>
+              <!-- <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip></el-table-column> -->
+              <!-- <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip></el-table-column> -->
+              <!-- <el-table-column label="所在建筑楼层">
+                <template slot-scope="scope">
+                  <el-tooltip :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
+                    placement="top">
+                    <div class="tool-tip">
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column> -->
+              <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip></el-table-column> -->
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="rightRecoList.length&&!leftAI&&lArray.length">
+              <div class="r-title">
+                可能对应的{{typeName}}实例
+                <i class="el-icon-close" @click="rightRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in rightRecoList" :key="t.id" class="r-ai-recom-item ai-recom-item" @click="selectAI(i,'r')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.ObjectLocalName}}</p>
+                <p>
+                  {{t.BuildLocalName&&t.BuildLocalName!='null'&&t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:''}}
+                </p>
+                <p>{{t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:''}}</p>
+                <p>{{t.InstallLocation&&t.InstallLocation!='null'?t.InstallLocation:''}}</p>
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+    </div>
+    <!-- 历史记录弹窗 -->
+    <history-dialog ref="history" @delSuc="deleteSuc" :typeName="'space'"></history-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import historyDialog from "@/components/point/dynamicdata/historyDialog";
+import locationCas from "@/components/point/dynamicdata/locationCascader";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import { mapGetters, mapActions } from "vuex";
+import {
+  dynamicPendingobjs,
+  dynamicPendingPoint,
+  dynamicCreateRelation,
+  dynamicDeleteRelation,
+  dynamicPointTypeList,
+  dynamicQueryAI
+} from "@/api/scan/request";
+export default {
+  components: {
+    historyDialog,
+    locationCas,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  data() {
+    return {
+      isSwitch: true, //AI辅助
+      typeName: "", //当前类型
+      LtableData: [], //列表数据
+      RtableData: [], //列表数据
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [], //数据源
+        ObjectLocalName: "", //部件实例关键字
+        locationVal: [] //所在位置下拉
+      }, //查询条件
+      leftRecoList: [], //左推荐
+      rightRecoList: [], //右推荐
+      leftAI: true, //左右推荐公用-互斥
+      lArray: [], //左侧选中数据
+      rArray: [], //右侧选中数据
+      hasRequestedFlag: false, //用于标识是否关联过数据,若是则重新请求第一页数据,
+      showPlus: false, //+1记录动画
+      num: 0, //记录条数
+      timer: null, //函数节流 延时器
+      historyList: [], //操作历史
+      lTableLoading: false, //loading
+      rTableLoading: false,
+      lPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      },
+      rPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      }
+    };
+  },
+  created() {
+    this.typeName = this.$route.query.typeName;
+    this.init();
+  },
+  mounted() {
+    this.lTableBody = this.$refs.lTableBody.bodyWrapper;
+    this.rTableBody = this.$refs.rTableBody.bodyWrapper;
+
+    this.lTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.lTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.lTableBody.clientHeight || this.lTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.lTableBody.scrollHeight || this.lTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        if (this.lPage.pageNumber * this.lPage.pageSize < this.lPage.total) {
+          this.lPage.pageNumber++;
+          if (this.LtableData.length < 50) {
+            this.lPage.pageNumber = 1
+          }
+          this.getLeftData();
+        }
+      }
+    });
+    this.rTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.rTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.rTableBody.clientHeight || this.rTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.rTableBody.scrollHeight || this.rTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        // 获取到的不是全部数据 当滚动到底部 继续获取新的数据
+        if (this.rPage.pageNumber * this.rPage.pageSize < this.rPage.total) {
+          this.rPage.pageNumber++;
+          if (this.RtableData.length < 50) {
+            this.rPage.pageNumber = 1
+          }
+          this.getRightData();
+        }
+      }
+    });
+  },
+  computed: {
+    ...mapGetters("layout", ["projectId"]),
+    requestFlag() {
+      return this.lArray.length > 0 && this.rArray.length > 0;
+    }
+  },
+  methods: {
+    init() {
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //切换AI辅助
+    changeType() { },
+    //推荐中的选择事件
+    selectAI(i, l) {
+      //i 索引 l 左侧AI or 右侧AI
+      if (l == "l") {
+        this.leftRecoList[i].checked = true;
+        this.lArray = [];
+        this.lArray.push(this.leftRecoList[i]);
+      } else {
+        this.rightRecoList[i].checked = true;
+        this.rArray = [];
+        this.rArray.push(this.rightRecoList[i]);
+      }
+    },
+    //查看本次历史记录
+    showHistory() {
+      this.$refs.history.showDialog(this.historyList);
+    },
+    //重新获取数据
+    reGetData() {
+      //删除选中项
+      this.delSelectedRows(this.lArray, this.rArray);
+      let history = { leftList: this.lArray, rightList: this.rArray };
+      this.historyList.push(history); //保存本次操作记录
+      this.lArray = []; //清空选中list
+      this.rArray = [];
+      this.num++; //右上角操作记录
+      this.showPlus = true;
+      setTimeout(() => {
+        this.showPlus = false;
+      }, 800);
+    },
+    //少于20条请求数据-否则删除选中项
+    delSelectedRows(lArray, rArray) {
+      for (let li = this.LtableData.length - 1; li >= 0; li--) {
+        for (let lj = 0; lj < lArray.length; lj++) {
+          if (this.LtableData[li] && (this.LtableData[li].EquipmentMark == lArray[lj].EquipmentMark && this.LtableData[li].SubTypeCode == lArray[lj].SubTypeCode)) {
+            // this.LtableData.splice(i, 1)
+            delete this.LtableData[li]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.LtableData = this.LtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      for (let ri = this.RtableData.length - 1; ri >= 0; ri--) {
+        for (let rj = 0; rj < rArray.length; rj++) {
+          if (this.RtableData[ri] && (this.RtableData[ri].ObjectID == rArray[rj].ObjectID && this.RtableData[ri].SubTypeCode == rArray[rj].SubTypeCode)) {
+            // this.RtableData.splice(i, 1)
+            delete this.RtableData[ri]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.RtableData = this.RtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      if (this.LtableData.length < 20 && this.lPage.Total > 49) {
+        this.queryLeftTable()
+      }
+      if (this.RtableData.length < 20 && this.rPage.Total > 49) {
+        this.queryRightTable()
+      }
+    },
+    //表格中的选中事件
+    leftSelectionChange(selection) {
+      this.lArray = selection;
+    },
+    rightSelectionChange(selection) {
+      this.rArray = selection;
+    },
+    //查询左侧列表
+    queryLeftTable() {
+      this.lPage.pageNumber = 1;
+      if (this.$refs.lTableBody && this.$refs.lTableBody.bodyWrapper) {
+        this.$refs.lTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getLeftData();
+    },
+    //查询右侧列表
+    queryRightTable() {
+      this.rPage.pageNumber = 1;
+      if (this.$refs.rTableBody && this.$refs.rTableBody.bodyWrapper) {
+        this.$refs.rTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getRightData();
+    },
+    //获取待关联点位-左侧
+    getLeftData() {
+      let param = {
+        PageNumber: this.lPage.pageNumber,
+        PageSize: this.lPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件(目前只是单个查,需后台支持)
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.lTableLoading = true;
+      dynamicPendingPoint(param, res => {
+        this.lPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.lPage.pageNumber == 1) {
+          this.LtableData = res.Content;
+        } else {
+          this.LtableData = this.LtableData.concat(res.Content);
+        }
+        this.lTableLoading = false;
+      });
+    },
+    //获取待关联实例-右侧
+    getRightData() {
+      let param = {
+        PageNumber: this.rPage.pageNumber,
+        PageSize: this.rPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.ObjectLocalName && this.form.ObjectLocalName.length) {
+        param.ObjectLocalName = this.form.ObjectLocalName;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.locationVal.length > 0) {
+        param.BuildLocalName = this.form.locationVal[0];
+      }
+      if (this.form.locationVal.length > 1) {
+        param.FloorLocalName = this.form.locationVal[1];
+      }
+      if (this.form.locationVal.length > 2) {
+        param.SpaceType = this.form.locationVal[2];
+      }
+      if (this.form.locationVal.length > 3) {
+        param.RoomLocalName = this.form.locationVal[3];
+      }
+      if (!this.form.locationVal.length) {
+        delete param.BuildLocalName
+        delete param.FloorLocalName
+        delete param.SpaceType
+        delete param.RoomLocalName
+      }
+      this.rTableLoading = true;
+      dynamicPendingobjs(param, res => {
+        this.rPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.rPage.pageNumber == 1) {
+          this.RtableData = res.Content;
+        } else {
+          this.RtableData = this.RtableData.concat(res.Content);
+        }
+        this.rTableLoading = false;
+      });
+    },
+    //清除对应关系成功
+    deleteSuc() {
+      this.num--;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //所在位置修改
+    changeLocation(val) {
+      this.form.locationVal = val;
+      this.queryRightTable();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.form.SubTypeName = val;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.form.Datasource = val;
+      this.queryLeftTable();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.form.LocationFlag = val;
+      this.queryLeftTable();
+    },
+    //返回
+    goback() {
+      this.$router.push({ path: '/point/dynamicdata', query: { typeName: this.typeName } })
+    },
+    //点击行
+    clickLeftRow(row, column, event) {
+      this.$refs.lTableBody.toggleRowSelection(row);
+    },
+    //点击行
+    clickRightRow(row, column, event) {
+      this.$refs.rTableBody.toggleRowSelection(row);
+    },
+  },
+  watch: {
+    projectId(n, o) {
+      this.init();
+    },
+    //判断两边是否均有选中值-创建关联
+    requestFlag(n, o) {
+      if (n) {
+        this.rightRecoList = [];
+        this.leftRecoList = [];
+        let object = {
+          Objs: this.rArray,
+          Points: this.lArray
+        };
+        let param = [];
+        param.push(object);
+        setTimeout(() => {
+          this.reGetData();
+        }, 200)
+        dynamicCreateRelation(param, res => {
+          if (res.Result == "success") {
+            this.$message.success("关联成功");
+          }
+        });
+      }
+    },
+    //AI辅助-左侧
+    lArray(n, o) {
+      //先清空,返回结果后赋值
+      this.rightRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Points: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.rightRecoList = res.Content[0].Objs;
+            this.leftAI = !this.rightRecoList.length
+          })
+        }, 800)
+      } else {
+        this.rightRecoList = []
+        this.leftAI = true;
+      }
+    },
+    //AI辅助-右侧
+    rArray(n, o) {
+      this.leftRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Objs: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.leftRecoList = res.Content[0].Points;
+            this.leftAI = !!this.leftRecoList.length
+          })
+        }, 800)
+      } else {
+        this.leftRecoList = []
+        this.leftAI = false
+      }
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@import "../addRelationCommon.scss";
+</style>
+<style>
+body {
+  overflow-y: hidden;
+}
+</style>

+ 7 - 171
src/views/point/dynamicdata/addRelation/equipRela/index.vue

@@ -4,7 +4,7 @@
     <el-row>
       <el-col :span="12">
         <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
-        <div style="display:inline-block;width:317px;margin-left:10px;">
+        <div style="display:inline-block;width:345px;margin-left:10px;">
           <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
         </div>
       </el-col>
@@ -54,7 +54,8 @@
                 </template>
               </el-table-column>
               <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column>
-              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5"></el-table-column>
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
               <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
             </el-table>
           </div>
@@ -106,10 +107,11 @@
               <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip></el-table-column>
               <el-table-column label="所在建筑楼层">
                 <template slot-scope="scope">
-                  <el-tooltip :content="scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''"
+                  <el-tooltip
+                    :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
                     placement="top">
                     <div class="tool-tip">
-                      {{scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''}}</div>
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
                   </el-tooltip>
                 </template>
               </el-table-column>
@@ -546,173 +548,7 @@ export default {
 };
 </script>
 <style lang="scss" scoped>
-#addRelation {
-  height: 100%;
-  background-color: #fff;
-  padding: 10px 20px 10px 10px;
-  .text-right {
-    text-align: right;
-  }
-  /deep/ .table-container {
-    height: calc(100% - 160px);
-    & > div {
-      width: 49.5%;
-      float: left;
-    }
-    .left-table,
-    .right-table {
-      height: 100%;
-      h5 {
-        line-height: 44px;
-        padding-left: 5px;
-        background-color: #e1e4e5;
-        color: #2b2b2b
-      }
-      .query-box {
-        margin-bottom: 20px;
-        .query-item {
-          float: left;
-          width: 30%;
-          margin: 5px;
-          label {
-            display: block;
-            margin-bottom: 5px;
-          }
-          .el-select {
-            width: 100%;
-          }
-        }
-      }
-      .table-box {
-        height: calc(100% - 50px);
-        position: relative;
-        .AIRecom {
-          position: absolute;
-          width: 96%;
-          height: 160px;
-          bottom: 5px;
-          left: 1%;
-          background-color: rgba(255, 255, 255, 0.9);
-          z-index: 3;
-          font-size: 12px;
-          .r-title {
-            padding-left: 10px;
-            i {
-              float: right;
-            }
-          }
-          .l-title {
-            text-align: right;
-            padding-right: 10px;
-            i {
-              float: left;
-            }
-          }
-          .ai-recom-item {
-            width: 30%;
-            height: 126px;
-            float: left;
-            border: 1px solid #ebeef5;
-            overflow: hidden;
-            padding-top: 5px;
-            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-            p {
-              position: relative;
-              white-space: nowrap;
-              text-overflow: ellipsis;
-              overflow: hidden;
-              padding: 0 8px;
-              z-index: -1;
-              span {
-                float: right;
-              }
-              .el-checkbox {
-                margin-right: 0;
-              }
-            }
-          }
-          .ai-recom-item.r-ai-recom-item + .ai-recom-item {
-            margin-left: 3%;
-          }
-          .l-ai-recom-item {
-            float: right;
-            p {
-              text-align: right;
-              padding-right: 10px;
-              span {
-                float: left;
-              }
-            }
-          }
-          .ai-recom-item.l-ai-recom-item + .ai-recom-item {
-            margin-right: 3%;
-          }
-        }
-      }
-      ::-webkit-scrollbar {
-        display: none;
-      }
-      //列表
-      .custom-table {
-        height: 100%;
-        .bgf5 {
-          background-color: #f5f7fa;
-        }
-        thead label.el-checkbox {
-          display: none;
-        }
-      }
-      .l-custom-table {
-        td {
-          text-align: right;
-        }
-      }
-      .tool-tip {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-    }
-  }
-  .fr {
-    padding-top: 10px;
-    float: right;
-  }
-  .clearfix:before,
-  .clearfix:after {
-    content: "";
-    display: block;
-    clear: both;
-  }
-  .clearfix {
-    zoom: 1;
-  }
-  .ani-his-plus {
-    position: relative;
-    .plusOne {
-      position: absolute;
-      color: red;
-      left: 50%;
-      bottom: 50%;
-      font-size: 20px;
-      font-weight: 800;
-    }
-    .startAni {
-      animation: fadeToTop 1s;
-    }
-  }
-  @keyframes fadeToTop {
-    0% {
-      left: 50%;
-      bottom: 50%;
-      opacity: 1;
-    }
-    100% {
-      bottom: 100%;
-      opacity: 0;
-    }
-  }
-}
+@import "../addRelationCommon.scss";
 </style>
 <style>
 body {

+ 558 - 0
src/views/point/dynamicdata/addRelation/floorRela/index.vue

@@ -0,0 +1,558 @@
+<template>
+  <div id="addRelation">
+    <!-- 数据字典对象类型 -->
+    <el-row style="margin-bottom:16px;">
+      <el-col :span="12">
+        <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
+        <!-- <div style="display:inline-block;width:345px;margin-left:10px;">
+          <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
+        </div> -->
+      </el-col>
+      <el-col :span="12" class="text-right">
+        <el-switch @change="changeType" v-model="isSwitch"></el-switch>
+        <span style="padding:0 5px;">AI辅助</span>
+        <el-button size="medium" @click="showHistory" class="ani-his-plus">
+          本次对应记录 {{num}}
+          <span v-if="showPlus" :class="{'plusOne':true,'startAni':showPlus}">+1</span>
+        </el-button>
+      </el-col>
+    </el-row>
+    <!-- 列表及查询条件区域 -->
+    <div class="table-container clearfix">
+      <div class="left-table">
+        <h5>未对应的{{typeName}}标识</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}标识关键字</label>
+            <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryLeftTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryLeftTable"></i>
+            </el-input>
+          </div>
+          <div class="query-item">
+            <label>数据源</label>
+            <dataSource :Related="false" @change="changeDataSource" :typeName="typeName"></dataSource>
+          </div>
+          <div class="query-item">
+            <label>位置标签</label>
+            <locationFlag :Related="false" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+          </div>
+        </div>
+        <!-- 左侧列表 -->
+        <div class="table-box">
+          <div class="l-custom-table custom-table" v-loading="lTableLoading">
+            <el-table ref="lTableBody" :data="LtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickLeftRow"
+              @selection-change="leftSelectionChange">
+              <el-table-column label="数据源" align="right" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.Datasource }}</template>
+              </el-table-column>
+              <el-table-column label="位置标签" align="right">
+                <template slot-scope="scope">
+                  <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+                    <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column>
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="leftRecoList.length&&leftAI&&rArray.length">
+              <div class="l-title">
+                可能对应的{{typeName}}标识
+                <i class="el-icon-close" @click="leftRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in leftRecoList" :key="t.id" class="l-ai-recom-item ai-recom-item" @click="selectAI(i,'l')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.EquipmentMark&&t.EquipmentMark!='null'?t.EquipmentMark:''}}</p>
+                <p>{{t.LocationFlag.toString()}}</p>
+                <!-- <p>{{t.address}}</p> -->
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+      <div class="right-table" style="float: right;">
+        <h5>未对应的{{typeName}}实例</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}实例关键字</label>
+            <el-input :placeholder="`请输入${typeName}实例关键字`" v-model="form.ObjectLocalName" @keyup.enter.native="queryRightTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryRightTable"></i>
+            </el-input>
+          </div>
+          <div class="query-item">
+            <label>所属建筑</label>
+            <location-cas @change="changeLocation" :Related="false" :typeName="typeName" :isFloor="true"></location-cas>
+          </div>
+        </div>
+        <!-- 右侧列表 -->
+        <div class="table-box">
+          <div class="r-custom-table custom-table" v-loading="rTableLoading">
+            <el-table ref="rTableBody" :data="RtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickRightRow"
+              @selection-change="rightSelectionChange">
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+              <el-table-column :label="`${typeName}实例名称`" width="200" class-name="bgf5" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.ObjectLocalName }}</template>
+              </el-table-column>
+              <el-table-column prop="ObjectLocalCode" :label="`${typeName}本地编码`" show-overflow-tooltip></el-table-column>
+              <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip></el-table-column>
+              <!-- <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip></el-table-column> -->
+              <el-table-column label="所在建筑楼层">
+                <template slot-scope="scope">
+                  <el-tooltip
+                    :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
+                    placement="top">
+                    <div class="tool-tip">
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip></el-table-column> -->
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="rightRecoList.length&&!leftAI&&lArray.length">
+              <div class="r-title">
+                可能对应的{{typeName}}实例
+                <i class="el-icon-close" @click="rightRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in rightRecoList" :key="t.id" class="r-ai-recom-item ai-recom-item" @click="selectAI(i,'r')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.ObjectLocalName}}</p>
+                <p>
+                  {{t.BuildLocalName&&t.BuildLocalName!='null'&&t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:''}}
+                </p>
+                <p>{{t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:''}}</p>
+                <p>{{t.InstallLocation&&t.InstallLocation!='null'?t.InstallLocation:''}}</p>
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+    </div>
+    <!-- 历史记录弹窗 -->
+    <history-dialog ref="history" @delSuc="deleteSuc" :typeName="'space'"></history-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import historyDialog from "@/components/point/dynamicdata/historyDialog";
+import locationCas from "@/components/point/dynamicdata/locationCascader";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import { mapGetters, mapActions } from "vuex";
+import {
+  dynamicPendingobjs,
+  dynamicPendingPoint,
+  dynamicCreateRelation,
+  dynamicDeleteRelation,
+  dynamicPointTypeList,
+  dynamicQueryAI
+} from "@/api/scan/request";
+export default {
+  components: {
+    historyDialog,
+    locationCas,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  data() {
+    return {
+      isSwitch: true, //AI辅助
+      typeName: "", //当前类型
+      LtableData: [], //列表数据
+      RtableData: [], //列表数据
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [], //数据源
+        ObjectLocalName: "", //部件实例关键字
+        locationVal: [] //所在位置下拉
+      }, //查询条件
+      leftRecoList: [], //左推荐
+      rightRecoList: [], //右推荐
+      leftAI: true, //左右推荐公用-互斥
+      lArray: [], //左侧选中数据
+      rArray: [], //右侧选中数据
+      hasRequestedFlag: false, //用于标识是否关联过数据,若是则重新请求第一页数据,
+      showPlus: false, //+1记录动画
+      num: 0, //记录条数
+      timer: null, //函数节流 延时器
+      historyList: [], //操作历史
+      lTableLoading: false, //loading
+      rTableLoading: false,
+      lPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      },
+      rPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      }
+    };
+  },
+  created() {
+    this.typeName = this.$route.query.typeName;
+    this.init();
+  },
+  mounted() {
+    this.lTableBody = this.$refs.lTableBody.bodyWrapper;
+    this.rTableBody = this.$refs.rTableBody.bodyWrapper;
+
+    this.lTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.lTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.lTableBody.clientHeight || this.lTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.lTableBody.scrollHeight || this.lTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        if (this.lPage.pageNumber * this.lPage.pageSize < this.lPage.total) {
+          this.lPage.pageNumber++;
+          if (this.LtableData.length < 50) {
+            this.lPage.pageNumber = 1
+          }
+          this.getLeftData();
+        }
+      }
+    });
+    this.rTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.rTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.rTableBody.clientHeight || this.rTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.rTableBody.scrollHeight || this.rTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        // 获取到的不是全部数据 当滚动到底部 继续获取新的数据
+        if (this.rPage.pageNumber * this.rPage.pageSize < this.rPage.total) {
+          this.rPage.pageNumber++;
+          if (this.RtableData.length < 50) {
+            this.rPage.pageNumber = 1
+          }
+          this.getRightData();
+        }
+      }
+    });
+  },
+  computed: {
+    ...mapGetters("layout", ["projectId"]),
+    requestFlag() {
+      return this.lArray.length > 0 && this.rArray.length > 0;
+    }
+  },
+  methods: {
+    init() {
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //切换AI辅助
+    changeType() { },
+    //推荐中的选择事件
+    selectAI(i, l) {
+      //i 索引 l 左侧AI or 右侧AI
+      if (l == "l") {
+        this.leftRecoList[i].checked = true;
+        this.lArray = [];
+        this.lArray.push(this.leftRecoList[i]);
+      } else {
+        this.rightRecoList[i].checked = true;
+        this.rArray = [];
+        this.rArray.push(this.rightRecoList[i]);
+      }
+    },
+    //查看本次历史记录
+    showHistory() {
+      this.$refs.history.showDialog(this.historyList);
+    },
+    //重新获取数据
+    reGetData() {
+      //删除选中项
+      this.delSelectedRows(this.lArray, this.rArray);
+      let history = { leftList: this.lArray, rightList: this.rArray };
+      this.historyList.push(history); //保存本次操作记录
+      this.lArray = []; //清空选中list
+      this.rArray = [];
+      this.num++; //右上角操作记录
+      this.showPlus = true;
+      setTimeout(() => {
+        this.showPlus = false;
+      }, 800);
+    },
+    //少于20条请求数据-否则删除选中项
+    delSelectedRows(lArray, rArray) {
+      for (let li = this.LtableData.length - 1; li >= 0; li--) {
+        for (let lj = 0; lj < lArray.length; lj++) {
+          if (this.LtableData[li] && (this.LtableData[li].EquipmentMark == lArray[lj].EquipmentMark && this.LtableData[li].SubTypeCode == lArray[lj].SubTypeCode)) {
+            // this.LtableData.splice(i, 1)
+            delete this.LtableData[li]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.LtableData = this.LtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      for (let ri = this.RtableData.length - 1; ri >= 0; ri--) {
+        for (let rj = 0; rj < rArray.length; rj++) {
+          if (this.RtableData[ri] && (this.RtableData[ri].ObjectID == rArray[rj].ObjectID && this.RtableData[ri].SubTypeCode == rArray[rj].SubTypeCode)) {
+            // this.RtableData.splice(i, 1)
+            delete this.RtableData[ri]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.RtableData = this.RtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      if (this.LtableData.length < 20 && this.lPage.Total > 49) {
+        this.queryLeftTable()
+      }
+      if (this.RtableData.length < 20 && this.rPage.Total > 49) {
+        this.queryRightTable()
+      }
+    },
+    //表格中的选中事件
+    leftSelectionChange(selection) {
+      this.lArray = selection;
+    },
+    rightSelectionChange(selection) {
+      this.rArray = selection;
+    },
+    //查询左侧列表
+    queryLeftTable() {
+      this.lPage.pageNumber = 1;
+      if (this.$refs.lTableBody && this.$refs.lTableBody.bodyWrapper) {
+        this.$refs.lTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getLeftData();
+    },
+    //查询右侧列表
+    queryRightTable() {
+      this.rPage.pageNumber = 1;
+      if (this.$refs.rTableBody && this.$refs.rTableBody.bodyWrapper) {
+        this.$refs.rTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getRightData();
+    },
+    //获取待关联点位-左侧
+    getLeftData() {
+      let param = {
+        PageNumber: this.lPage.pageNumber,
+        PageSize: this.lPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件(目前只是单个查,需后台支持)
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.lTableLoading = true;
+      dynamicPendingPoint(param, res => {
+        this.lPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.lPage.pageNumber == 1) {
+          this.LtableData = res.Content;
+        } else {
+          this.LtableData = this.LtableData.concat(res.Content);
+        }
+        this.lTableLoading = false;
+      });
+    },
+    //获取待关联实例-右侧
+    getRightData() {
+      let param = {
+        PageNumber: this.rPage.pageNumber,
+        PageSize: this.rPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.ObjectLocalName && this.form.ObjectLocalName.length) {
+        param.ObjectLocalName = this.form.ObjectLocalName;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.locationVal.length > 0) {
+        param.BuildLocalName = this.form.locationVal[0];
+      }
+      if (this.form.locationVal.length > 1) {
+        param.FloorLocalName = this.form.locationVal[1];
+      }
+      if (this.form.locationVal.length > 2) {
+        param.SpaceType = this.form.locationVal[2];
+      }
+      if (this.form.locationVal.length > 3) {
+        param.RoomLocalName = this.form.locationVal[3];
+      }
+      if (!this.form.locationVal.length) {
+        delete param.BuildLocalName
+        delete param.FloorLocalName
+        delete param.SpaceType
+        delete param.RoomLocalName
+      }
+      this.rTableLoading = true;
+      dynamicPendingobjs(param, res => {
+        this.rPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.rPage.pageNumber == 1) {
+          this.RtableData = res.Content;
+        } else {
+          this.RtableData = this.RtableData.concat(res.Content);
+        }
+        this.rTableLoading = false;
+      });
+    },
+    //清除对应关系成功
+    deleteSuc() {
+      this.num--;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //所在位置修改
+    changeLocation(val) {
+      this.form.locationVal = val;
+      this.queryRightTable();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.form.SubTypeName = val;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.form.Datasource = val;
+      this.queryLeftTable();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.form.LocationFlag = val;
+      this.queryLeftTable();
+    },
+    //返回
+    goback() {
+      this.$router.push({ path: '/point/dynamicdata', query: { typeName: this.typeName } })
+    },
+    //点击行
+    clickLeftRow(row, column, event) {
+      this.$refs.lTableBody.toggleRowSelection(row);
+    },
+    //点击行
+    clickRightRow(row, column, event) {
+      this.$refs.rTableBody.toggleRowSelection(row);
+    },
+  },
+  watch: {
+    projectId(n, o) {
+      this.init();
+    },
+    //判断两边是否均有选中值-创建关联
+    requestFlag(n, o) {
+      if (n) {
+        this.rightRecoList = [];
+        this.leftRecoList = [];
+        let object = {
+          Objs: this.rArray,
+          Points: this.lArray
+        };
+        let param = [];
+        param.push(object);
+        setTimeout(() => {
+          this.reGetData();
+        }, 200)
+        dynamicCreateRelation(param, res => {
+          if (res.Result == "success") {
+            this.$message.success("关联成功");
+          }
+        });
+      }
+    },
+    //AI辅助-左侧
+    lArray(n, o) {
+      //先清空,返回结果后赋值
+      this.rightRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Points: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.rightRecoList = res.Content[0].Objs;
+            this.leftAI = !this.rightRecoList.length
+          })
+        }, 800)
+      } else {
+        this.rightRecoList = []
+        this.leftAI = true;
+      }
+    },
+    //AI辅助-右侧
+    rArray(n, o) {
+      this.leftRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Objs: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.leftRecoList = res.Content[0].Points;
+            this.leftAI = !!this.leftRecoList.length
+          })
+        }, 800)
+      } else {
+        this.leftRecoList = []
+        this.leftAI = false
+      }
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@import "../addRelationCommon.scss";
+</style>
+<style>
+body {
+  overflow-y: hidden;
+}
+</style>

+ 7 - 171
src/views/point/dynamicdata/addRelation/partsRela/index.vue

@@ -4,7 +4,7 @@
     <el-row>
       <el-col :span="12">
         <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
-        <div style="display:inline-block;width:317px;margin-left:10px;">
+        <div style="display:inline-block;width:345px;margin-left:10px;">
           <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
         </div>
       </el-col>
@@ -54,7 +54,8 @@
                 </template>
               </el-table-column>
               <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column>
-              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5"></el-table-column>
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
               <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
             </el-table>
           </div>
@@ -107,10 +108,11 @@
               <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip></el-table-column>
               <el-table-column label="所在建筑楼层">
                 <template slot-scope="scope">
-                  <el-tooltip :content="scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''"
+                  <el-tooltip
+                    :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
                     placement="top">
                     <div class="tool-tip">
-                      {{scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''}}</div>
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
                   </el-tooltip>
                 </template>
               </el-table-column>
@@ -547,173 +549,7 @@ export default {
 };
 </script>
 <style lang="scss" scoped>
-#addRelation {
-  height: 100%;
-  background-color: #fff;
-  padding: 10px 20px 10px 10px;
-  .text-right {
-    text-align: right;
-  }
-  /deep/ .table-container {
-    height: calc(100% - 160px);
-    & > div {
-      width: 49.5%;
-      float: left;
-    }
-    .left-table,
-    .right-table {
-      height: 100%;
-      h5 {
-        line-height: 44px;
-        padding-left: 5px;
-        background-color: #e1e4e5;
-        color: #2b2b2b
-      }
-      .query-box {
-        margin-bottom: 20px;
-        .query-item {
-          float: left;
-          width: 30%;
-          margin: 5px;
-          label {
-            display: block;
-            margin-bottom: 5px;
-          }
-          .el-select {
-            width: 100%;
-          }
-        }
-      }
-      .table-box {
-        height: calc(100% - 50px);
-        position: relative;
-        .AIRecom {
-          position: absolute;
-          width: 96%;
-          height: 160px;
-          bottom: 5px;
-          left: 1%;
-          background-color: rgba(255, 255, 255, 0.9);
-          z-index: 3;
-          font-size: 12px;
-          .r-title {
-            padding-left: 10px;
-            i {
-              float: right;
-            }
-          }
-          .l-title {
-            text-align: right;
-            padding-right: 10px;
-            i {
-              float: left;
-            }
-          }
-          .ai-recom-item {
-            width: 30%;
-            height: 126px;
-            float: left;
-            border: 1px solid #ebeef5;
-            overflow: hidden;
-            padding-top: 5px;
-            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-            p {
-              position: relative;
-              white-space: nowrap;
-              text-overflow: ellipsis;
-              overflow: hidden;
-              padding: 0 8px;
-              z-index: -1;
-              span {
-                float: right;
-              }
-              .el-checkbox {
-                margin-right: 0;
-              }
-            }
-          }
-          .ai-recom-item.r-ai-recom-item + .ai-recom-item {
-            margin-left: 3%;
-          }
-          .l-ai-recom-item {
-            float: right;
-            p {
-              text-align: right;
-              padding-right: 10px;
-              span {
-                float: left;
-              }
-            }
-          }
-          .ai-recom-item.l-ai-recom-item + .ai-recom-item {
-            margin-right: 3%;
-          }
-        }
-      }
-      ::-webkit-scrollbar {
-        display: none;
-      }
-      //列表
-      .custom-table {
-        height: 100%;
-        .bgf5 {
-          background-color: #f5f7fa;
-        }
-        thead label.el-checkbox {
-          display: none;
-        }
-      }
-      .l-custom-table {
-        td {
-          text-align: right;
-        }
-      }
-      .tool-tip {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-    }
-  }
-  .fr {
-    padding-top: 10px;
-    float: right;
-  }
-  .clearfix:before,
-  .clearfix:after {
-    content: "";
-    display: block;
-    clear: both;
-  }
-  .clearfix {
-    zoom: 1;
-  }
-  .ani-his-plus {
-    position: relative;
-    .plusOne {
-      position: absolute;
-      color: red;
-      left: 50%;
-      bottom: 50%;
-      font-size: 20px;
-      font-weight: 800;
-    }
-    .startAni {
-      animation: fadeToTop 1s;
-    }
-  }
-  @keyframes fadeToTop {
-    0% {
-      left: 50%;
-      bottom: 50%;
-      opacity: 1;
-    }
-    100% {
-      bottom: 100%;
-      opacity: 0;
-    }
-  }
-}
+@import "../addRelationCommon.scss";
 </style>
 <style>
 body {

+ 558 - 0
src/views/point/dynamicdata/addRelation/projectRela/index.vue

@@ -0,0 +1,558 @@
+<template>
+  <div id="addRelation">
+    <!-- 数据字典对象类型 -->
+    <el-row style="margin-bottom:16px;">
+      <el-col :span="12">
+        <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
+        <!-- <div style="display:inline-block;width:345px;margin-left:10px;">
+          <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
+        </div> -->
+      </el-col>
+      <el-col :span="12" class="text-right">
+        <el-switch @change="changeType" v-model="isSwitch"></el-switch>
+        <span style="padding:0 5px;">AI辅助</span>
+        <el-button size="medium" @click="showHistory" class="ani-his-plus">
+          本次对应记录 {{num}}
+          <span v-if="showPlus" :class="{'plusOne':true,'startAni':showPlus}">+1</span>
+        </el-button>
+      </el-col>
+    </el-row>
+    <!-- 列表及查询条件区域 -->
+    <div class="table-container clearfix">
+      <div class="left-table">
+        <h5>未对应的{{typeName}}标识</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}标识关键字</label>
+            <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryLeftTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryLeftTable"></i>
+            </el-input>
+          </div>
+          <div class="query-item">
+            <label>数据源</label>
+            <dataSource :Related="false" @change="changeDataSource" :typeName="typeName"></dataSource>
+          </div>
+          <div class="query-item">
+            <label>位置标签</label>
+            <locationFlag :Related="false" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+          </div>
+        </div>
+        <!-- 左侧列表 -->
+        <div class="table-box">
+          <div class="l-custom-table custom-table" v-loading="lTableLoading">
+            <el-table ref="lTableBody" :data="LtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickLeftRow"
+              @selection-change="leftSelectionChange">
+              <el-table-column label="数据源" align="right" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.Datasource }}</template>
+              </el-table-column>
+              <el-table-column label="位置标签" align="right">
+                <template slot-scope="scope">
+                  <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+                    <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column>
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="leftRecoList.length&&leftAI&&rArray.length">
+              <div class="l-title">
+                可能对应的{{typeName}}标识
+                <i class="el-icon-close" @click="leftRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in leftRecoList" :key="t.id" class="l-ai-recom-item ai-recom-item" @click="selectAI(i,'l')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.EquipmentMark&&t.EquipmentMark!='null'?t.EquipmentMark:''}}</p>
+                <p>{{t.LocationFlag.toString()}}</p>
+                <!-- <p>{{t.address}}</p> -->
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+      <div class="right-table" style="float: right;">
+        <h5>未对应的{{typeName}}实例</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}实例关键字</label>
+            <el-input :placeholder="`请输入${typeName}实例关键字`" v-model="form.ObjectLocalName" @keyup.enter.native="queryRightTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryRightTable"></i>
+            </el-input>
+          </div>
+          <div class="query-item">
+            <label>所在位置</label>
+            <location-cas @change="changeLocation" :Related="false" :typeName="typeName"></location-cas>
+          </div>
+        </div>
+        <!-- 右侧列表 -->
+        <div class="table-box">
+          <div class="r-custom-table custom-table" v-loading="rTableLoading">
+            <el-table ref="rTableBody" :data="RtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickRightRow"
+              @selection-change="rightSelectionChange">
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+              <el-table-column :label="`${typeName}实例名称`" width="200" class-name="bgf5" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.ObjectLocalName }}</template>
+              </el-table-column>
+              <el-table-column prop="ObjectLocalCode" :label="`${typeName}本地编码`" show-overflow-tooltip></el-table-column>
+              <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip></el-table-column>
+              <!-- <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip></el-table-column> -->
+              <el-table-column label="所在建筑楼层">
+                <template slot-scope="scope">
+                  <el-tooltip
+                    :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
+                    placement="top">
+                    <div class="tool-tip">
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip></el-table-column> -->
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="rightRecoList.length&&!leftAI&&lArray.length">
+              <div class="r-title">
+                可能对应的{{typeName}}实例
+                <i class="el-icon-close" @click="rightRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in rightRecoList" :key="t.id" class="r-ai-recom-item ai-recom-item" @click="selectAI(i,'r')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.ObjectLocalName}}</p>
+                <p>
+                  {{t.BuildLocalName&&t.BuildLocalName!='null'&&t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:''}}
+                </p>
+                <p>{{t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:''}}</p>
+                <p>{{t.InstallLocation&&t.InstallLocation!='null'?t.InstallLocation:''}}</p>
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+    </div>
+    <!-- 历史记录弹窗 -->
+    <history-dialog ref="history" @delSuc="deleteSuc" :typeName="'space'"></history-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import historyDialog from "@/components/point/dynamicdata/historyDialog";
+import locationCas from "@/components/point/dynamicdata/locationCascader";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import { mapGetters, mapActions } from "vuex";
+import {
+  dynamicPendingobjs,
+  dynamicPendingPoint,
+  dynamicCreateRelation,
+  dynamicDeleteRelation,
+  dynamicPointTypeList,
+  dynamicQueryAI
+} from "@/api/scan/request";
+export default {
+  components: {
+    historyDialog,
+    locationCas,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  data() {
+    return {
+      isSwitch: true, //AI辅助
+      typeName: "", //当前类型
+      LtableData: [], //列表数据
+      RtableData: [], //列表数据
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [], //数据源
+        ObjectLocalName: "", //部件实例关键字
+        locationVal: [] //所在位置下拉
+      }, //查询条件
+      leftRecoList: [], //左推荐
+      rightRecoList: [], //右推荐
+      leftAI: true, //左右推荐公用-互斥
+      lArray: [], //左侧选中数据
+      rArray: [], //右侧选中数据
+      hasRequestedFlag: false, //用于标识是否关联过数据,若是则重新请求第一页数据,
+      showPlus: false, //+1记录动画
+      num: 0, //记录条数
+      timer: null, //函数节流 延时器
+      historyList: [], //操作历史
+      lTableLoading: false, //loading
+      rTableLoading: false,
+      lPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      },
+      rPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      }
+    };
+  },
+  created() {
+    this.typeName = this.$route.query.typeName;
+    this.init();
+  },
+  mounted() {
+    this.lTableBody = this.$refs.lTableBody.bodyWrapper;
+    this.rTableBody = this.$refs.rTableBody.bodyWrapper;
+
+    this.lTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.lTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.lTableBody.clientHeight || this.lTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.lTableBody.scrollHeight || this.lTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        if (this.lPage.pageNumber * this.lPage.pageSize < this.lPage.total) {
+          this.lPage.pageNumber++;
+          if (this.LtableData.length < 50) {
+            this.lPage.pageNumber = 1
+          }
+          this.getLeftData();
+        }
+      }
+    });
+    this.rTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.rTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.rTableBody.clientHeight || this.rTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.rTableBody.scrollHeight || this.rTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        // 获取到的不是全部数据 当滚动到底部 继续获取新的数据
+        if (this.rPage.pageNumber * this.rPage.pageSize < this.rPage.total) {
+          this.rPage.pageNumber++;
+          if (this.RtableData.length < 50) {
+            this.rPage.pageNumber = 1
+          }
+          this.getRightData();
+        }
+      }
+    });
+  },
+  computed: {
+    ...mapGetters("layout", ["projectId"]),
+    requestFlag() {
+      return this.lArray.length > 0 && this.rArray.length > 0;
+    }
+  },
+  methods: {
+    init() {
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //切换AI辅助
+    changeType() { },
+    //推荐中的选择事件
+    selectAI(i, l) {
+      //i 索引 l 左侧AI or 右侧AI
+      if (l == "l") {
+        this.leftRecoList[i].checked = true;
+        this.lArray = [];
+        this.lArray.push(this.leftRecoList[i]);
+      } else {
+        this.rightRecoList[i].checked = true;
+        this.rArray = [];
+        this.rArray.push(this.rightRecoList[i]);
+      }
+    },
+    //查看本次历史记录
+    showHistory() {
+      this.$refs.history.showDialog(this.historyList);
+    },
+    //重新获取数据
+    reGetData() {
+      //删除选中项
+      this.delSelectedRows(this.lArray, this.rArray);
+      let history = { leftList: this.lArray, rightList: this.rArray };
+      this.historyList.push(history); //保存本次操作记录
+      this.lArray = []; //清空选中list
+      this.rArray = [];
+      this.num++; //右上角操作记录
+      this.showPlus = true;
+      setTimeout(() => {
+        this.showPlus = false;
+      }, 800);
+    },
+    //少于20条请求数据-否则删除选中项
+    delSelectedRows(lArray, rArray) {
+      for (let li = this.LtableData.length - 1; li >= 0; li--) {
+        for (let lj = 0; lj < lArray.length; lj++) {
+          if (this.LtableData[li] && (this.LtableData[li].EquipmentMark == lArray[lj].EquipmentMark && this.LtableData[li].SubTypeCode == lArray[lj].SubTypeCode)) {
+            // this.LtableData.splice(i, 1)
+            delete this.LtableData[li]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.LtableData = this.LtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      for (let ri = this.RtableData.length - 1; ri >= 0; ri--) {
+        for (let rj = 0; rj < rArray.length; rj++) {
+          if (this.RtableData[ri] && (this.RtableData[ri].ObjectID == rArray[rj].ObjectID && this.RtableData[ri].SubTypeCode == rArray[rj].SubTypeCode)) {
+            // this.RtableData.splice(i, 1)
+            delete this.RtableData[ri]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.RtableData = this.RtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      if (this.LtableData.length < 20 && this.lPage.Total > 49) {
+        this.queryLeftTable()
+      }
+      if (this.RtableData.length < 20 && this.rPage.Total > 49) {
+        this.queryRightTable()
+      }
+    },
+    //表格中的选中事件
+    leftSelectionChange(selection) {
+      this.lArray = selection;
+    },
+    rightSelectionChange(selection) {
+      this.rArray = selection;
+    },
+    //查询左侧列表
+    queryLeftTable() {
+      this.lPage.pageNumber = 1;
+      if (this.$refs.lTableBody && this.$refs.lTableBody.bodyWrapper) {
+        this.$refs.lTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getLeftData();
+    },
+    //查询右侧列表
+    queryRightTable() {
+      this.rPage.pageNumber = 1;
+      if (this.$refs.rTableBody && this.$refs.rTableBody.bodyWrapper) {
+        this.$refs.rTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getRightData();
+    },
+    //获取待关联点位-左侧
+    getLeftData() {
+      let param = {
+        PageNumber: this.lPage.pageNumber,
+        PageSize: this.lPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件(目前只是单个查,需后台支持)
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.lTableLoading = true;
+      dynamicPendingPoint(param, res => {
+        this.lPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.lPage.pageNumber == 1) {
+          this.LtableData = res.Content;
+        } else {
+          this.LtableData = this.LtableData.concat(res.Content);
+        }
+        this.lTableLoading = false;
+      });
+    },
+    //获取待关联实例-右侧
+    getRightData() {
+      let param = {
+        PageNumber: this.rPage.pageNumber,
+        PageSize: this.rPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.ObjectLocalName && this.form.ObjectLocalName.length) {
+        param.ObjectLocalName = this.form.ObjectLocalName;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.locationVal.length > 0) {
+        param.BuildLocalName = this.form.locationVal[0];
+      }
+      if (this.form.locationVal.length > 1) {
+        param.FloorLocalName = this.form.locationVal[1];
+      }
+      if (this.form.locationVal.length > 2) {
+        param.SpaceType = this.form.locationVal[2];
+      }
+      if (this.form.locationVal.length > 3) {
+        param.RoomLocalName = this.form.locationVal[3];
+      }
+      if (!this.form.locationVal.length) {
+        delete param.BuildLocalName
+        delete param.FloorLocalName
+        delete param.SpaceType
+        delete param.RoomLocalName
+      }
+      this.rTableLoading = true;
+      dynamicPendingobjs(param, res => {
+        this.rPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.rPage.pageNumber == 1) {
+          this.RtableData = res.Content;
+        } else {
+          this.RtableData = this.RtableData.concat(res.Content);
+        }
+        this.rTableLoading = false;
+      });
+    },
+    //清除对应关系成功
+    deleteSuc() {
+      this.num--;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //所在位置修改
+    changeLocation(val) {
+      this.form.locationVal = val;
+      this.queryRightTable();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.form.SubTypeName = val;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.form.Datasource = val;
+      this.queryLeftTable();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.form.LocationFlag = val;
+      this.queryLeftTable();
+    },
+    //返回
+    goback() {
+      this.$router.push({ path: '/point/dynamicdata', query: { typeName: this.typeName } })
+    },
+    //点击行
+    clickLeftRow(row, column, event) {
+      this.$refs.lTableBody.toggleRowSelection(row);
+    },
+    //点击行
+    clickRightRow(row, column, event) {
+      this.$refs.rTableBody.toggleRowSelection(row);
+    },
+  },
+  watch: {
+    projectId(n, o) {
+      this.init();
+    },
+    //判断两边是否均有选中值-创建关联
+    requestFlag(n, o) {
+      if (n) {
+        this.rightRecoList = [];
+        this.leftRecoList = [];
+        let object = {
+          Objs: this.rArray,
+          Points: this.lArray
+        };
+        let param = [];
+        param.push(object);
+        setTimeout(() => {
+          this.reGetData();
+        }, 200)
+        dynamicCreateRelation(param, res => {
+          if (res.Result == "success") {
+            this.$message.success("关联成功");
+          }
+        });
+      }
+    },
+    //AI辅助-左侧
+    lArray(n, o) {
+      //先清空,返回结果后赋值
+      this.rightRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Points: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.rightRecoList = res.Content[0].Objs;
+            this.leftAI = !this.rightRecoList.length
+          })
+        }, 800)
+      } else {
+        this.rightRecoList = []
+        this.leftAI = true;
+      }
+    },
+    //AI辅助-右侧
+    rArray(n, o) {
+      this.leftRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Objs: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.leftRecoList = res.Content[0].Points;
+            this.leftAI = !!this.leftRecoList.length
+          })
+        }, 800)
+      } else {
+        this.leftRecoList = []
+        this.leftAI = false
+      }
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@import "../addRelationCommon.scss";
+</style>
+<style>
+body {
+  overflow-y: hidden;
+}
+</style>

+ 7 - 171
src/views/point/dynamicdata/addRelation/spaceRela/index.vue

@@ -4,7 +4,7 @@
     <el-row>
       <el-col :span="12">
         <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
-        <div style="display:inline-block;width:317px;margin-left:10px;">
+        <div style="display:inline-block;width:345px;margin-left:10px;">
           <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
         </div>
       </el-col>
@@ -54,7 +54,8 @@
                 </template>
               </el-table-column>
               <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column>
-              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5"></el-table-column>
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
               <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
             </el-table>
           </div>
@@ -107,10 +108,11 @@
               <!-- <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip></el-table-column> -->
               <el-table-column label="所在建筑楼层">
                 <template slot-scope="scope">
-                  <el-tooltip :content="scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''"
+                  <el-tooltip
+                    :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
                     placement="top">
                     <div class="tool-tip">
-                      {{scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''}}</div>
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
                   </el-tooltip>
                 </template>
               </el-table-column>
@@ -547,173 +549,7 @@ export default {
 };
 </script>
 <style lang="scss" scoped>
-#addRelation {
-  height: 100%;
-  background-color: #fff;
-  padding: 10px 20px 10px 10px;
-  .text-right {
-    text-align: right;
-  }
-  /deep/ .table-container {
-    height: calc(100% - 160px);
-    & > div {
-      width: 49.5%;
-      float: left;
-    }
-    .left-table,
-    .right-table {
-      height: 100%;
-      h5 {
-        line-height: 44px;
-        padding-left: 5px;
-        background-color: #e1e4e5;
-        color: #2b2b2b
-      }
-      .query-box {
-        margin-bottom: 20px;
-        .query-item {
-          float: left;
-          width: 30%;
-          margin: 5px;
-          label {
-            display: block;
-            margin-bottom: 5px;
-          }
-          .el-select {
-            width: 100%;
-          }
-        }
-      }
-      .table-box {
-        height: calc(100% - 50px);
-        position: relative;
-        .AIRecom {
-          position: absolute;
-          width: 96%;
-          height: 160px;
-          bottom: 5px;
-          left: 1%;
-          background-color: rgba(255, 255, 255, 0.9);
-          z-index: 3;
-          font-size: 12px;
-          .r-title {
-            padding-left: 10px;
-            i {
-              float: right;
-            }
-          }
-          .l-title {
-            text-align: right;
-            padding-right: 10px;
-            i {
-              float: left;
-            }
-          }
-          .ai-recom-item {
-            width: 30%;
-            height: 126px;
-            float: left;
-            border: 1px solid #ebeef5;
-            overflow: hidden;
-            padding-top: 5px;
-            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-            p {
-              position: relative;
-              white-space: nowrap;
-              text-overflow: ellipsis;
-              overflow: hidden;
-              padding: 0 8px;
-              z-index: -1;
-              span {
-                float: right;
-              }
-              .el-checkbox {
-                margin-right: 0;
-              }
-            }
-          }
-          .ai-recom-item.r-ai-recom-item + .ai-recom-item {
-            margin-left: 3%;
-          }
-          .l-ai-recom-item {
-            float: right;
-            p {
-              text-align: right;
-              padding-right: 10px;
-              span {
-                float: left;
-              }
-            }
-          }
-          .ai-recom-item.l-ai-recom-item + .ai-recom-item {
-            margin-right: 3%;
-          }
-        }
-      }
-      ::-webkit-scrollbar {
-        display: none;
-      }
-      //列表
-      .custom-table {
-        height: 100%;
-        .bgf5 {
-          background-color: #f5f7fa;
-        }
-        thead label.el-checkbox {
-          display: none;
-        }
-      }
-      .l-custom-table {
-        td {
-          text-align: right;
-        }
-      }
-      .tool-tip {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-    }
-  }
-  .fr {
-    padding-top: 10px;
-    float: right;
-  }
-  .clearfix:before,
-  .clearfix:after {
-    content: "";
-    display: block;
-    clear: both;
-  }
-  .clearfix {
-    zoom: 1;
-  }
-  .ani-his-plus {
-    position: relative;
-    .plusOne {
-      position: absolute;
-      color: red;
-      left: 50%;
-      bottom: 50%;
-      font-size: 20px;
-      font-weight: 800;
-    }
-    .startAni {
-      animation: fadeToTop 1s;
-    }
-  }
-  @keyframes fadeToTop {
-    0% {
-      left: 50%;
-      bottom: 50%;
-      opacity: 1;
-    }
-    100% {
-      bottom: 100%;
-      opacity: 0;
-    }
-  }
-}
+@import "../addRelationCommon.scss";
 </style>
 <style>
 body {

+ 6 - 171
src/views/point/dynamicdata/addRelation/systemRela/index.vue

@@ -4,7 +4,7 @@
     <el-row>
       <el-col :span="12">
         <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
-        <div style="display:inline-block;width:317px;margin-left:10px;">
+        <div style="display:inline-block;width:345px;margin-left:10px;">
           <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
         </div>
       </el-col>
@@ -54,7 +54,8 @@
                 </template>
               </el-table-column>
               <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column>
-              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5"></el-table-column>
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
               <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
             </el-table>
           </div>
@@ -107,10 +108,10 @@
               <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip></el-table-column>
               <el-table-column label="所在建筑楼层">
                 <template slot-scope="scope">
-                  <el-tooltip :content="scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''"
+                  <el-tooltip :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
                     placement="top">
                     <div class="tool-tip">
-                      {{scope.row.BuildLocalName?scope.row.BuildLocalName+'-':''+scope.row.FloorLocalName?scope.row.FloorLocalName:''}}</div>
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
                   </el-tooltip>
                 </template>
               </el-table-column>
@@ -547,173 +548,7 @@ export default {
 };
 </script>
 <style lang="scss" scoped>
-#addRelation {
-  height: 100%;
-  background-color: #fff;
-  padding: 10px 20px 10px 10px;
-  .text-right {
-    text-align: right;
-  }
-  /deep/ .table-container {
-    height: calc(100% - 160px);
-    & > div {
-      width: 49.5%;
-      float: left;
-    }
-    .left-table,
-    .right-table {
-      height: 100%;
-      h5 {
-        line-height: 44px;
-        padding-left: 5px;
-        background-color: #e1e4e5;
-        color: #2b2b2b
-      }
-      .query-box {
-        margin-bottom: 20px;
-        .query-item {
-          float: left;
-          width: 30%;
-          margin: 5px;
-          label {
-            display: block;
-            margin-bottom: 5px;
-          }
-          .el-select {
-            width: 100%;
-          }
-        }
-      }
-      .table-box {
-        height: calc(100% - 50px);
-        position: relative;
-        .AIRecom {
-          position: absolute;
-          width: 96%;
-          height: 160px;
-          bottom: 5px;
-          left: 1%;
-          background-color: rgba(255, 255, 255, 0.9);
-          z-index: 3;
-          font-size: 12px;
-          .r-title {
-            padding-left: 10px;
-            i {
-              float: right;
-            }
-          }
-          .l-title {
-            text-align: right;
-            padding-right: 10px;
-            i {
-              float: left;
-            }
-          }
-          .ai-recom-item {
-            width: 30%;
-            height: 126px;
-            float: left;
-            border: 1px solid #ebeef5;
-            overflow: hidden;
-            padding-top: 5px;
-            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
-            p {
-              position: relative;
-              white-space: nowrap;
-              text-overflow: ellipsis;
-              overflow: hidden;
-              padding: 0 8px;
-              z-index: -1;
-              span {
-                float: right;
-              }
-              .el-checkbox {
-                margin-right: 0;
-              }
-            }
-          }
-          .ai-recom-item.r-ai-recom-item + .ai-recom-item {
-            margin-left: 3%;
-          }
-          .l-ai-recom-item {
-            float: right;
-            p {
-              text-align: right;
-              padding-right: 10px;
-              span {
-                float: left;
-              }
-            }
-          }
-          .ai-recom-item.l-ai-recom-item + .ai-recom-item {
-            margin-right: 3%;
-          }
-        }
-      }
-      ::-webkit-scrollbar {
-        display: none;
-      }
-      //列表
-      .custom-table {
-        height: 100%;
-        .bgf5 {
-          background-color: #f5f7fa;
-        }
-        thead label.el-checkbox {
-          display: none;
-        }
-      }
-      .l-custom-table {
-        td {
-          text-align: right;
-        }
-      }
-      .tool-tip {
-        white-space: nowrap;
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
-    }
-  }
-  .fr {
-    padding-top: 10px;
-    float: right;
-  }
-  .clearfix:before,
-  .clearfix:after {
-    content: "";
-    display: block;
-    clear: both;
-  }
-  .clearfix {
-    zoom: 1;
-  }
-  .ani-his-plus {
-    position: relative;
-    .plusOne {
-      position: absolute;
-      color: red;
-      left: 50%;
-      bottom: 50%;
-      font-size: 20px;
-      font-weight: 800;
-    }
-    .startAni {
-      animation: fadeToTop 1s;
-    }
-  }
-  @keyframes fadeToTop {
-    0% {
-      left: 50%;
-      bottom: 50%;
-      opacity: 1;
-    }
-    100% {
-      bottom: 100%;
-      opacity: 0;
-    }
-  }
-}
+@import "../addRelationCommon.scss";
 </style>
 <style>
 body {

+ 558 - 0
src/views/point/dynamicdata/addRelation/tenantRela/index.vue

@@ -0,0 +1,558 @@
+<template>
+  <div id="addRelation">
+    <!-- 数据字典对象类型 -->
+    <el-row style="margin-bottom:16px;">
+      <el-col :span="12">
+        <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
+        <!-- <div style="display:inline-block;width:345px;margin-left:10px;">
+          <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
+        </div> -->
+      </el-col>
+      <el-col :span="12" class="text-right">
+        <el-switch @change="changeType" v-model="isSwitch"></el-switch>
+        <span style="padding:0 5px;">AI辅助</span>
+        <el-button size="medium" @click="showHistory" class="ani-his-plus">
+          本次对应记录 {{num}}
+          <span v-if="showPlus" :class="{'plusOne':true,'startAni':showPlus}">+1</span>
+        </el-button>
+      </el-col>
+    </el-row>
+    <!-- 列表及查询条件区域 -->
+    <div class="table-container clearfix">
+      <div class="left-table">
+        <h5>未对应的{{typeName}}标识</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}标识关键字</label>
+            <el-input :placeholder="`请输入${typeName}标识关键字`" v-model="form.EquipmentMark" @keyup.enter.native="queryLeftTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryLeftTable"></i>
+            </el-input>
+          </div>
+          <div class="query-item">
+            <label>数据源</label>
+            <dataSource :Related="false" @change="changeDataSource" :typeName="typeName"></dataSource>
+          </div>
+          <div class="query-item">
+            <label>位置标签</label>
+            <locationFlag :Related="false" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
+          </div>
+        </div>
+        <!-- 左侧列表 -->
+        <div class="table-box">
+          <div class="l-custom-table custom-table" v-loading="lTableLoading">
+            <el-table ref="lTableBody" :data="LtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickLeftRow"
+              @selection-change="leftSelectionChange">
+              <el-table-column label="数据源" align="right" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.Datasource }}</template>
+              </el-table-column>
+              <el-table-column label="位置标签" align="right">
+                <template slot-scope="scope">
+                  <el-tooltip :content="scope.row.LocationFlag.toString()" placement="top">
+                    <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <el-table-column prop="SubTypeName" :label="`${typeName}类型关键字`" align="right" show-overflow-tooltip></el-table-column>
+              <el-table-column prop="EquipmentMark" :label="`${typeName}标识`" show-overflow-tooltip align="right" width="200" class-name="bgf5">
+              </el-table-column>
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="leftRecoList.length&&leftAI&&rArray.length">
+              <div class="l-title">
+                可能对应的{{typeName}}标识
+                <i class="el-icon-close" @click="leftRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in leftRecoList" :key="t.id" class="l-ai-recom-item ai-recom-item" @click="selectAI(i,'l')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.EquipmentMark&&t.EquipmentMark!='null'?t.EquipmentMark:''}}</p>
+                <p>{{t.LocationFlag.toString()}}</p>
+                <!-- <p>{{t.address}}</p> -->
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+      <div class="right-table" style="float: right;">
+        <h5>未对应的{{typeName}}实例</h5>
+        <!-- 查询条件 -->
+        <div class="query-box clearfix">
+          <div class="query-item">
+            <label>{{typeName}}实例关键字</label>
+            <el-input :placeholder="`请输入${typeName}实例关键字`" v-model="form.ObjectLocalName" @keyup.enter.native="queryRightTable">
+              <i slot="suffix" class="el-input__icon el-icon-search" @click="queryRightTable"></i>
+            </el-input>
+          </div>
+          <div class="query-item">
+            <label>所在位置</label>
+            <location-cas @change="changeLocation" :Related="false" :typeName="typeName"></location-cas>
+          </div>
+        </div>
+        <!-- 右侧列表 -->
+        <div class="table-box">
+          <div class="r-custom-table custom-table" v-loading="rTableLoading">
+            <el-table ref="rTableBody" :data="RtableData" tooltip-effect="dark" style="width: 100%" height="100%" @row-click="clickRightRow"
+              @selection-change="rightSelectionChange">
+              <el-table-column type="selection" width="35" class-name="bgf5"></el-table-column>
+              <el-table-column :label="`${typeName}实例名称`" width="200" class-name="bgf5" show-overflow-tooltip>
+                <template slot-scope="scope">{{ scope.row.ObjectLocalName }}</template>
+              </el-table-column>
+              <el-table-column prop="ObjectLocalCode" :label="`${typeName}本地编码`" show-overflow-tooltip></el-table-column>
+              <el-table-column prop="SubTypeName" :label="`${typeName}类型`" show-overflow-tooltip></el-table-column>
+              <!-- <el-table-column prop="CascadeEquipLocalName" label="所属设备实例" show-overflow-tooltip></el-table-column> -->
+              <el-table-column label="所在建筑楼层">
+                <template slot-scope="scope">
+                  <el-tooltip
+                    :content="(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')"
+                    placement="top">
+                    <div class="tool-tip">
+                      {{(scope.row.BuildLocalName?scope.row.BuildLocalName+'-':'')+(scope.row.FloorLocalName?scope.row.FloorLocalName:'')}}</div>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+              <!-- <el-table-column prop="RoomLocalName" label="所在业务空间" show-overflow-tooltip></el-table-column> -->
+            </el-table>
+          </div>
+          <!-- 智能推荐 -->
+          <transition name="el-zoom-in-top">
+            <div class="AIRecom" v-show="rightRecoList.length&&!leftAI&&lArray.length">
+              <div class="r-title">
+                可能对应的{{typeName}}实例
+                <i class="el-icon-close" @click="rightRecoList=[]"></i>
+              </div>
+              <div v-for="(t,i) in rightRecoList" :key="t.id" class="r-ai-recom-item ai-recom-item" @click="selectAI(i,'r')">
+                <p>
+                  <el-checkbox v-model="t.checked"></el-checkbox>
+                  <span>{{(t.Proximity*100).toFixed(2)}}%</span>
+                </p>
+                <p>{{t.ObjectLocalName}}</p>
+                <p>
+                  {{t.BuildLocalName&&t.BuildLocalName!='null'&&t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:''}}
+                </p>
+                <p>{{t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:''}}</p>
+                <p>{{t.InstallLocation&&t.InstallLocation!='null'?t.InstallLocation:''}}</p>
+              </div>
+            </div>
+          </transition>
+        </div>
+      </div>
+    </div>
+    <!-- 历史记录弹窗 -->
+    <history-dialog ref="history" @delSuc="deleteSuc" :typeName="'space'"></history-dialog>
+  </div>
+</template>
+<script>
+import tools from "@/utils/tools";
+import historyDialog from "@/components/point/dynamicdata/historyDialog";
+import locationCas from "@/components/point/dynamicdata/locationCascader";
+import dictionaryDevice from "@/components/point/dynamicdata/dictionaryDevice";
+import dataSource from "@/components/point/dynamicdata/dataSource";
+import locationFlag from "@/components/point/dynamicdata/locationFlag";
+import { mapGetters, mapActions } from "vuex";
+import {
+  dynamicPendingobjs,
+  dynamicPendingPoint,
+  dynamicCreateRelation,
+  dynamicDeleteRelation,
+  dynamicPointTypeList,
+  dynamicQueryAI
+} from "@/api/scan/request";
+export default {
+  components: {
+    historyDialog,
+    locationCas,
+    dictionaryDevice,
+    dataSource,
+    locationFlag
+  },
+  data() {
+    return {
+      isSwitch: true, //AI辅助
+      typeName: "", //当前类型
+      LtableData: [], //列表数据
+      RtableData: [], //列表数据
+      form: {
+        EquipmentMark: "", //部件标识关键字
+        SubTypeName: [], //数据字典部件类型
+        LocationFlag: [], //位置标签
+        Datasource: [], //数据源
+        ObjectLocalName: "", //部件实例关键字
+        locationVal: [] //所在位置下拉
+      }, //查询条件
+      leftRecoList: [], //左推荐
+      rightRecoList: [], //右推荐
+      leftAI: true, //左右推荐公用-互斥
+      lArray: [], //左侧选中数据
+      rArray: [], //右侧选中数据
+      hasRequestedFlag: false, //用于标识是否关联过数据,若是则重新请求第一页数据,
+      showPlus: false, //+1记录动画
+      num: 0, //记录条数
+      timer: null, //函数节流 延时器
+      historyList: [], //操作历史
+      lTableLoading: false, //loading
+      rTableLoading: false,
+      lPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      },
+      rPage: {
+        pageNumber: 1,
+        pageSize: 50,
+        total: 0
+      }
+    };
+  },
+  created() {
+    this.typeName = this.$route.query.typeName;
+    this.init();
+  },
+  mounted() {
+    this.lTableBody = this.$refs.lTableBody.bodyWrapper;
+    this.rTableBody = this.$refs.rTableBody.bodyWrapper;
+
+    this.lTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.lTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.lTableBody.clientHeight || this.lTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.lTableBody.scrollHeight || this.lTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        if (this.lPage.pageNumber * this.lPage.pageSize < this.lPage.total) {
+          this.lPage.pageNumber++;
+          if (this.LtableData.length < 50) {
+            this.lPage.pageNumber = 1
+          }
+          this.getLeftData();
+        }
+      }
+    });
+    this.rTableBody.addEventListener("scroll", () => {
+      // 滚动距离
+      let scrollTop = this.rTableBody.scrollTop;
+      // 变量windowHeight是可视区的高度
+      let windowHeight =
+        this.rTableBody.clientHeight || this.rTableBody.clientHeight;
+      // 变量scrollHeight是滚动条的总高度
+      let scrollHeight =
+        this.rTableBody.scrollHeight || this.rTableBody.scrollHeight;
+      if (scrollTop + windowHeight === scrollHeight) {
+        // 获取到的不是全部数据 当滚动到底部 继续获取新的数据
+        if (this.rPage.pageNumber * this.rPage.pageSize < this.rPage.total) {
+          this.rPage.pageNumber++;
+          if (this.RtableData.length < 50) {
+            this.rPage.pageNumber = 1
+          }
+          this.getRightData();
+        }
+      }
+    });
+  },
+  computed: {
+    ...mapGetters("layout", ["projectId"]),
+    requestFlag() {
+      return this.lArray.length > 0 && this.rArray.length > 0;
+    }
+  },
+  methods: {
+    init() {
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //切换AI辅助
+    changeType() { },
+    //推荐中的选择事件
+    selectAI(i, l) {
+      //i 索引 l 左侧AI or 右侧AI
+      if (l == "l") {
+        this.leftRecoList[i].checked = true;
+        this.lArray = [];
+        this.lArray.push(this.leftRecoList[i]);
+      } else {
+        this.rightRecoList[i].checked = true;
+        this.rArray = [];
+        this.rArray.push(this.rightRecoList[i]);
+      }
+    },
+    //查看本次历史记录
+    showHistory() {
+      this.$refs.history.showDialog(this.historyList);
+    },
+    //重新获取数据
+    reGetData() {
+      //删除选中项
+      this.delSelectedRows(this.lArray, this.rArray);
+      let history = { leftList: this.lArray, rightList: this.rArray };
+      this.historyList.push(history); //保存本次操作记录
+      this.lArray = []; //清空选中list
+      this.rArray = [];
+      this.num++; //右上角操作记录
+      this.showPlus = true;
+      setTimeout(() => {
+        this.showPlus = false;
+      }, 800);
+    },
+    //少于20条请求数据-否则删除选中项
+    delSelectedRows(lArray, rArray) {
+      for (let li = this.LtableData.length - 1; li >= 0; li--) {
+        for (let lj = 0; lj < lArray.length; lj++) {
+          if (this.LtableData[li] && (this.LtableData[li].EquipmentMark == lArray[lj].EquipmentMark && this.LtableData[li].SubTypeCode == lArray[lj].SubTypeCode)) {
+            // this.LtableData.splice(i, 1)
+            delete this.LtableData[li]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.LtableData = this.LtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      for (let ri = this.RtableData.length - 1; ri >= 0; ri--) {
+        for (let rj = 0; rj < rArray.length; rj++) {
+          if (this.RtableData[ri] && (this.RtableData[ri].ObjectID == rArray[rj].ObjectID && this.RtableData[ri].SubTypeCode == rArray[rj].SubTypeCode)) {
+            // this.RtableData.splice(i, 1)
+            delete this.RtableData[ri]
+            continue; //结束当前本轮循环,开始新的一轮循环
+          }
+        }
+      }
+      this.RtableData = this.RtableData.filter(item => {
+        return !(item == 'undefined')
+      })
+      if (this.LtableData.length < 20 && this.lPage.Total > 49) {
+        this.queryLeftTable()
+      }
+      if (this.RtableData.length < 20 && this.rPage.Total > 49) {
+        this.queryRightTable()
+      }
+    },
+    //表格中的选中事件
+    leftSelectionChange(selection) {
+      this.lArray = selection;
+    },
+    rightSelectionChange(selection) {
+      this.rArray = selection;
+    },
+    //查询左侧列表
+    queryLeftTable() {
+      this.lPage.pageNumber = 1;
+      if (this.$refs.lTableBody && this.$refs.lTableBody.bodyWrapper) {
+        this.$refs.lTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getLeftData();
+    },
+    //查询右侧列表
+    queryRightTable() {
+      this.rPage.pageNumber = 1;
+      if (this.$refs.rTableBody && this.$refs.rTableBody.bodyWrapper) {
+        this.$refs.rTableBody.bodyWrapper.scrollTop = 0
+      }
+      this.getRightData();
+    },
+    //获取待关联点位-左侧
+    getLeftData() {
+      let param = {
+        PageNumber: this.lPage.pageNumber,
+        PageSize: this.lPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件(目前只是单个查,需后台支持)
+      if (this.form.EquipmentMark && this.form.EquipmentMark.length) {
+        param.EquipmentMark = this.form.EquipmentMark;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.LocationFlag.length) {
+        param.LocationFlagList = this.form.LocationFlag;
+      }
+      if (this.form.Datasource.length) {
+        param.DatasourceList = this.form.Datasource;
+      }
+      this.lTableLoading = true;
+      dynamicPendingPoint(param, res => {
+        this.lPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.lPage.pageNumber == 1) {
+          this.LtableData = res.Content;
+        } else {
+          this.LtableData = this.LtableData.concat(res.Content);
+        }
+        this.lTableLoading = false;
+      });
+    },
+    //获取待关联实例-右侧
+    getRightData() {
+      let param = {
+        PageNumber: this.rPage.pageNumber,
+        PageSize: this.rPage.pageSize,
+        TypeNameList: [this.typeName]
+      };
+      //处理查询条件
+      if (this.form.ObjectLocalName && this.form.ObjectLocalName.length) {
+        param.ObjectLocalName = this.form.ObjectLocalName;
+      }
+      if (this.form.SubTypeName.length) {
+        param.SubTypeNameList = this.form.SubTypeName;
+      }
+      if (this.form.locationVal.length > 0) {
+        param.BuildLocalName = this.form.locationVal[0];
+      }
+      if (this.form.locationVal.length > 1) {
+        param.FloorLocalName = this.form.locationVal[1];
+      }
+      if (this.form.locationVal.length > 2) {
+        param.SpaceType = this.form.locationVal[2];
+      }
+      if (this.form.locationVal.length > 3) {
+        param.RoomLocalName = this.form.locationVal[3];
+      }
+      if (!this.form.locationVal.length) {
+        delete param.BuildLocalName
+        delete param.FloorLocalName
+        delete param.SpaceType
+        delete param.RoomLocalName
+      }
+      this.rTableLoading = true;
+      dynamicPendingobjs(param, res => {
+        this.rPage.total = res.PageSize < 50 ? res.PageSize : res.Total;
+        if (this.rPage.pageNumber == 1) {
+          this.RtableData = res.Content;
+        } else {
+          this.RtableData = this.RtableData.concat(res.Content);
+        }
+        this.rTableLoading = false;
+      });
+    },
+    //清除对应关系成功
+    deleteSuc() {
+      this.num--;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //所在位置修改
+    changeLocation(val) {
+      this.form.locationVal = val;
+      this.queryRightTable();
+    },
+    //数据字典部件类型修改
+    changeDictionary(val) {
+      this.form.SubTypeName = val;
+      this.queryLeftTable();
+      this.queryRightTable();
+    },
+    //数据源修改
+    changeDataSource(val) {
+      this.form.Datasource = val;
+      this.queryLeftTable();
+    },
+    //修改位置标签
+    changeLocationFlag(val) {
+      this.form.LocationFlag = val;
+      this.queryLeftTable();
+    },
+    //返回
+    goback() {
+      this.$router.push({ path: '/point/dynamicdata', query: { typeName: this.typeName } })
+    },
+    //点击行
+    clickLeftRow(row, column, event) {
+      this.$refs.lTableBody.toggleRowSelection(row);
+    },
+    //点击行
+    clickRightRow(row, column, event) {
+      this.$refs.rTableBody.toggleRowSelection(row);
+    },
+  },
+  watch: {
+    projectId(n, o) {
+      this.init();
+    },
+    //判断两边是否均有选中值-创建关联
+    requestFlag(n, o) {
+      if (n) {
+        this.rightRecoList = [];
+        this.leftRecoList = [];
+        let object = {
+          Objs: this.rArray,
+          Points: this.lArray
+        };
+        let param = [];
+        param.push(object);
+        setTimeout(() => {
+          this.reGetData();
+        }, 200)
+        dynamicCreateRelation(param, res => {
+          if (res.Result == "success") {
+            this.$message.success("关联成功");
+          }
+        });
+      }
+    },
+    //AI辅助-左侧
+    lArray(n, o) {
+      //先清空,返回结果后赋值
+      this.rightRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Points: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.rightRecoList = res.Content[0].Objs;
+            this.leftAI = !this.rightRecoList.length
+          })
+        }, 800)
+      } else {
+        this.rightRecoList = []
+        this.leftAI = true;
+      }
+    },
+    //AI辅助-右侧
+    rArray(n, o) {
+      this.leftRecoList = [];
+      clearTimeout(this.timer);
+      if (this.isSwitch && n.length) {
+        this.timer = setTimeout(() => {
+          let param = []
+          n.map(item => {
+            param.push({
+              Objs: [item],
+              Top: 3,
+              Proximity: 0.5
+            })
+          })
+          dynamicQueryAI(param, res => {
+            this.leftRecoList = res.Content[0].Points;
+            this.leftAI = !!this.leftRecoList.length
+          })
+        }, 800)
+      } else {
+        this.leftRecoList = []
+        this.leftAI = false
+      }
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@import "../addRelationCommon.scss";
+</style>
+<style>
+body {
+  overflow-y: hidden;
+}
+</style>

+ 20 - 0
src/views/point/dynamicdata/index.vue

@@ -39,6 +39,18 @@
       <!-- 空间 -->
       <spaceRules v-else-if="curType=='空间'" ref="space" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"
         @autoGroupRela="autoGroupRela"></spaceRules>
+      <!-- 建筑 -->
+      <buildRules v-else-if="curType=='建筑'" ref="build" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"
+        @autoGroupRela="autoGroupRela"></buildRules>
+      <!-- 楼层 -->
+      <floorRules v-else-if="curType=='楼层'" ref="floor" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"
+        @autoGroupRela="autoGroupRela"></floorRules>
+      <!-- 租户 -->
+      <tenantRules v-else-if="curType=='租户'" ref="tenant" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"
+        @autoGroupRela="autoGroupRela"></tenantRules>
+      <!-- 项目 -->
+      <projectRules v-else-if="curType=='项目'" ref="project" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"
+        @autoGroupRela="autoGroupRela"></projectRules>
       <!-- 开发中 -->
       <div v-else>开发中</div>
     </div>
@@ -92,6 +104,10 @@ import equipRules from "@/components/point/dynamicdata/equipRules";
 import partsRules from "@/components/point/dynamicdata/partsRules";
 import systemRules from "@/components/point/dynamicdata/systemRules";
 import spaceRules from "@/components/point/dynamicdata/spaceRules";
+import buildRules from "@/components/point/dynamicdata/buildRules";
+import floorRules from "@/components/point/dynamicdata/floorRules";
+import tenantRules from "@/components/point/dynamicdata/tenantRules";
+import projectRules from "@/components/point/dynamicdata/projectRules";
 import autoGroupRelaDialog from "@/components/point/dynamicdata/autoGroupRelaDialog";
 import {
   dynamicClashConfirm,
@@ -126,6 +142,10 @@ export default {
     partsRules,
     systemRules,
     spaceRules,
+    buildRules,
+    floorRules,
+    tenantRules,
+    projectRules,
     autoGroupRelaDialog
   },
   created() {