Просмотр исходного кода

Merge branch 'bfmanage' into infoPoint

zhangyu 5 лет назад
Родитель
Сommit
56bc44fe2e
30 измененных файлов с 3944 добавлено и 209 удалено
  1. 2 2
      package.json
  2. 30 0
      src/api/scan/request.js
  3. 1 1
      src/components/config_point/dictionaryCascader.vue
  4. 96 0
      src/components/point/dynamicdata/applyLog.vue
  5. 107 0
      src/components/point/dynamicdata/applyRulesDialog-copy.vue
  6. 1 1
      src/components/point/dynamicdata/applyRulesDialog.vue
  7. 1 1
      src/components/point/dynamicdata/dataSource.vue
  8. 63 21
      src/components/point/dynamicdata/delRelationDialog.vue
  9. 2 2
      src/components/point/dynamicdata/dictionaryDevice.vue
  10. 16 14
      src/components/point/dynamicdata/equipRules.vue
  11. 70 58
      src/components/point/dynamicdata/historyDialog.vue
  12. 1 1
      src/components/point/dynamicdata/locationCascader.vue
  13. 1 1
      src/components/point/dynamicdata/locationFlag.vue
  14. 54 37
      src/components/point/dynamicdata/partsRules.vue
  15. 269 0
      src/components/point/dynamicdata/spaceRules.vue
  16. 269 0
      src/components/point/dynamicdata/systemRules.vue
  17. 33 0
      src/components/ready/buildfloor/addBuild.vue
  18. 74 0
      src/components/ready/buildfloor/addConnectivity.vue
  19. 50 0
      src/components/ready/buildfloor/buildfloorCascader.vue
  20. 70 0
      src/components/ready/buildfloor/formItems.vue
  21. 21 0
      src/router/system.js
  22. 72 0
      src/utils/buildfloor/tools.js
  23. 12 4
      src/utils/hasontableUtils.js
  24. 39 30
      src/views/point/dynamicdata/addRelation/equipRela/index.vue
  25. 722 0
      src/views/point/dynamicdata/addRelation/partsRela/index.vue
  26. 722 0
      src/views/point/dynamicdata/addRelation/spaceRela/index.vue
  27. 722 0
      src/views/point/dynamicdata/addRelation/systemRela/index.vue
  28. 32 8
      src/views/point/dynamicdata/index.vue
  29. 106 0
      src/views/ready/buildfloor/addFloor/index.vue
  30. 286 28
      src/views/ready/buildfloor/index.vue

+ 2 - 2
package.json

@@ -12,7 +12,7 @@
     "dependencies": {
         "axios": "^0.18.0",
         "echarts": "^4.1.0",
-        "element-ui": "^2.6.1",
+        "element-ui": "^2.11.0",
         "font-awesome": "^4.7.0",
         "handsontable-pro": "^3.0.0",
         "jquery": "^3.3.1",
@@ -73,4 +73,4 @@
         "last 2 versions",
         "not ie <= 8"
     ]
-}
+}

+ 30 - 0
src/api/scan/request.js

@@ -1192,4 +1192,34 @@ export function dynamicQueryPrompt(param, success) {
 export function dynamicQueryAI(param, success) {
     let url = `${baseUrl}/pointconfig/dynamic/ai`;
     http.postJson(url, param, success)
+}
+
+//动态数据关联-执行对应结果详情
+export function dynamicExecuteDetail(param, success) {
+    let url = `${baseUrl}/pointconfig/dynamic/executeDetail`;
+    http.postJson(url, param, success)
+}
+
+//建筑楼层管理-创建楼层
+export function manageCreateFloor(param, success) {
+    let url = `${baseUrl}/datacenter/floor/create`;
+    http.postJson(url, param, success)
+}
+
+//建筑楼层管理-删除楼层
+export function manageDeleteFloor(param, success) {
+    let url = `${baseUrl}/datacenter/floor/delete`;
+    http.postJson(url, param, success)
+}
+
+//建筑楼层管理-修改楼层
+export function manageUpdateFloor(param, success) {
+    let url = `${baseUrl}/datacenter/floor/update`;
+    http.postJson(url, param, success)
+}
+
+//建筑楼层管理-创建楼层关联关系
+export function createRelationInFloor(param, success) {
+    let url = `${baseUrl}/datacenter/fl-through-fl/link-or`;
+    http.postJson(url, param, success)
 }

+ 1 - 1
src/components/config_point/dictionaryCascader.vue

@@ -167,7 +167,7 @@ export default {
       })
     },
     changeSelect(val) {
-      let labels = this.$refs.dictCas.currentLabels
+      let labels = this.$refs.dictCas.getCheckedNodes()[0].pathLabels
       this.$emit('change', { val: val, labels: labels })
     },
     //减少请求次数

+ 96 - 0
src/components/point/dynamicdata/applyLog.vue

@@ -0,0 +1,96 @@
+<template>
+  <el-dialog title="提示" :visible.sync="dialogVisible" width="40%" id="applyRules">
+    <p>按对应规则配置动态参数,已完成!</p>
+    <p>请检查并关注以下实例及相关动态参数,自动配置结果是否正确</p>
+    <div ref="message" style="max-height:400px;overflow-x:hidden;overflow-y:auto;">
+      <div v-for="item in ContentList" :key="item.code" v-show="item.children.length">
+        <p>【{{item.name}}】</p>
+        <div v-for="t in item.children" :key="t.ObjectID+t.Id">
+          <p v-if="t.TypeCode=='Eq'">
+            {{`${t.ObjectLocalName?t.ObjectLocalName:'--'}/${t.ObjectID?t.ObjectID:'--'}/${t.TypeName?t.TypeName:'--'}/${t.BuildLocalName?t.BuildLocalName:'--' }/${t.FloorLocalName?t.FloorLocalName:'--'}/${t.RoomLocalName?t.RoomLocalName:'--'}`}}
+          </p>
+          <p v-if="t.TypeCode=='Ec'">
+            {{`${t.ObjectLocalName?t.ObjectLocalName:'--'}/${t.ObjectID?t.ObjectID:'--'}/${t.TypeName?t.TypeName:'--'}/${t.CascadeEquipLocalName}/${t.BuildLocalName?t.BuildLocalName:'--' }/${t.FloorLocalName?t.FloorLocalName:'--'}/${t.RoomLocalName?t.RoomLocalName:'--'}`}}
+          </p>
+          <p v-if="t.TypeCode=='Sp'">
+            {{`${t.ObjectLocalName?t.ObjectLocalName:'--'}/${t.ObjectID?t.ObjectID:'--'}/${t.TypeName?t.TypeName:'--'}/${t.BuildLocalName?t.BuildLocalName:'--' }/${t.FloorLocalName?t.FloorLocalName:'--'}`}}
+          </p>
+          <p v-if="t.TypeCode=='Sy'">{{`${t.ObjectLocalName?t.ObjectLocalName:'--'}/${t.ObjectID?t.ObjectID:'--'}/${t.TypeName?t.TypeName:'--'}`}}</p>
+          <p>{{`${t.InfomationPoint}/${t.Old}/${t.New}`}}</p>
+          <hr style="margin:10px 0;width:50%;">
+        </div>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button size="small" @click="copy">复制信息</el-button>
+      <el-button size="small" type="primary" @click="dialogVisible=false">关闭</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+
+import {
+  dynamicExecuteDetail // 查询执行日志
+} from "@/api/scan/request";
+export default {
+  data() {
+    return {
+      dialogVisible: false, //弹窗显示与隐藏
+      ContentList: [
+        { name: '人工维护过值的信息点', children: [], code: 'MeterDiff' },
+        { name: '多个表号功能号对应一个信息点,无法执行对应规则,请检查', children: [], code: 'Multiple' },
+      ],
+      typeList: ["MeterDiff", "Multiple"], //此分类索引需与上list保持一致
+    };
+  },
+  methods: {
+    showDialog(Id) {
+      this.dialogVisible = true
+      this.getApplyLog(Id)
+    },
+    handleClose() {
+      this.dialogVisible = false;
+    },
+    //复制信息
+    copy() {
+      let text = this.$refs.message.innerText;
+      let copyUrl = $('<input type="text" />').val(text);//创建一个input框获取需要复制的文本内容
+      copyUrl.appendTo('body');
+      copyUrl.select();
+      document.execCommand("Copy");
+      copyUrl.remove()
+      this.$message.success('复制成功')
+    },
+    //查询执行日志
+    getApplyLog(Id) {
+      let param = {
+        Filters: `Type='Multiple' or Type='MeterDiff';Id='${Id}'`,
+        PageNumber: 1,
+        PageSize: 50
+      }
+      dynamicExecuteDetail(param, res => {
+        res.Content.map(t => {
+          this.ContentList[this.typeList.indexOf(t.Type)].children.push(t)
+        })
+        this.ContentList[1].children = this.unique(this.ContentList[1].children)
+      })
+    },
+    //数组去重
+    unique(arr) {
+      let hash = [];
+      if (arr.length) {
+        for (var i = 0; i < arr.length; i++) {
+          for (var j = i + 1; j < arr.length; j++) {
+            if (arr[i].ObjectID === arr[j].ObjectID && arr[i].InfomationPointCode === arr[j].InfomationPointCode) {
+              ++i;
+              arr[i].New += '/' + arr[j].New
+            }
+          }
+          hash.push(arr[i]);
+        }
+      }
+      return hash;
+    }
+  }
+};
+</script>

+ 107 - 0
src/components/point/dynamicdata/applyRulesDialog-copy.vue

@@ -0,0 +1,107 @@
+<template>
+  <el-dialog title="提示" :visible.sync="dialogVisible" width="30%" @close="handleClose" id="applyRules">
+    <p>对应规则有冲突,请选择处理方式:</p>
+    <div>
+      <el-radio v-model="select" :label="'Old'">保留设备实例中人工维护的动态参数的值</el-radio>
+    </div>
+    <div>
+      <el-radio v-model="select" :label="'New'">按照规则自动覆盖动态参数的值</el-radio>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button size="small" @click="handleClose">取消</el-button>
+      <el-button size="small" type="primary" @click="confirmClash">确认</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+
+import {
+  dynamicPromptobjs, //提示冲突
+  dynamicClashConfirm, //冲突选择确认
+  dynamicExecute, //执行规则
+} from "@/api/scan/request";
+import Vue from 'vue'
+export default {
+  data() {
+    return {
+      dialogVisible: false, //弹窗显示与隐藏
+      tableData: [],
+      activeName: "first", //当前所在tab页
+      finished: false, //未处理所有冲突提示文字
+      force: false, //请求冲突-默认false
+      select: 'New'
+    };
+  },
+  methods: {
+    showDialog(force) {
+      this.force = force
+      if (this.tableData.length) {
+        this.dialogVisible = true;
+      } else {
+        this.rulesConfirm()
+      }
+    },
+    handleClose() {
+      this.dialogVisible = false;
+      this.$emit('cancel')
+    },
+    //查询是否有冲突
+    getPromptobjs() {
+      let param = {
+        Force: this.force
+      }
+      dynamicPromptobjs(param, res => {
+        this.tableData = res.Content;
+        this.$emit('clash', this.tableData.length);
+      })
+    },
+    //确认冲突
+    confirmClash() {
+      let selectedRows = this.tableData.map(t => {
+        t.checked = this.select
+        return t
+      });
+      let param = [];
+      selectedRows.map(t => {
+        let tempObj = {
+          Objs: [t],
+          Points: [t]
+        }
+        param.push(tempObj)
+      })
+      dynamicClashConfirm(param, res => {
+        this.rulesConfirm();
+      })
+    },
+    //执行规则
+    rulesConfirm() {
+      let param = {
+        Force: this.force
+      }
+      dynamicExecute(param, res => {
+        this.$message.success('执行成功');
+        this.dialogVisible = false;
+        let missionID = res.Content[0].Id
+        this.$emit('refresh', missionID)
+      })
+    }
+  },
+  mounted() { },
+  created() {
+    this.getPromptobjs()
+  }
+};
+</script>
+<style lang="scss" scoped>
+#applyRules {
+  /deep/ td.bgf5 {
+    background-color: #f5f7fa;
+  }
+  td p {
+    text-align: center;
+  }
+  /deep/ .el-radio__input.is-checked + .el-radio__label {
+    color: #000;
+  }
+}
+</style>

+ 1 - 1
src/components/point/dynamicdata/applyRulesDialog.vue

@@ -38,7 +38,7 @@
       </el-table-column>
       <el-table-column label="设备实例本地名(本地编码)" class-name="bgf5" align="center">
         <template slot-scope="scope">
-          {{scope.row.EquipLocalName}}({{scope.row.ObjectID}})
+          {{scope.row.ObjectLocalName}}({{scope.row.ObjectID}})
         </template>
       </el-table-column>
       <el-table-column label="信息点" prop="InfomationPoint" align="center"></el-table-column>

+ 1 - 1
src/components/point/dynamicdata/dataSource.vue

@@ -27,7 +27,7 @@ export default {
   methods: {
     init() {
       this.Datasource = [];
-      let param = { Related: this.Related, typeName: this.typeName };
+      let param = { Related: this.Related, TypeName: this.typeName };
       //数据源
       dynamicDataSource(param, res => {
         if (res.Result == "success") {

+ 63 - 21
src/components/point/dynamicdata/delRelationDialog.vue

@@ -3,36 +3,37 @@
     <span>确定要清除对应关系?</span>
     <div v-loading="loading">
       <div v-show="ltableData.length>1">
-        <div style="margin:10px 0;">有多个设备标识对应了该设备实例,是否一并清除对应关系?</div>
+        <div style="margin:10px 0;">有多个对象标识对应了该对象实例,是否一并清除对应关系?</div>
         <el-row class="bgf5">
           <el-col :span="18">
             <el-table :data="ltableData" style="width: 100%;max-height:400px;" height="calc(100% - 160px)" @selection-change="changeSelection"
               ref="ltable">
-              <el-table-column label="设备标识" align="right" class-name="bgfff">
+              <el-table-column label="对象标识" align="right" class-name="bgfff">
                 <el-table-column label="数据源" prop="Datasource" align="right" class-name="bgfff" show-overflow-tooltip></el-table-column>
                 <el-table-column label="位置标签" prop="LocationFlag" align="right" class-name="bgfff" show-overflow-tooltip>
                   <template slot-scope="scope">
                     <div class="tool-tip">{{scope.row.LocationFlag.toString()}}</div>
                   </template>
                 </el-table-column>
-                <el-table-column label="设备标识" prop="EquipmentMark" align="right" class-name="bgf5" show-overflow-tooltip></el-table-column>
+                <el-table-column label="对象标识" prop="EquipmentMark" align="right" class-name="bgf5" show-overflow-tooltip></el-table-column>
                 <el-table-column type="selection" class-name="bgf5"></el-table-column>
               </el-table-column>
             </el-table>
           </el-col>
           <el-col :span="6">
-            <div class="singleDir pl-10 font-default">设备实例</div>
-            <div class="pl-10 font-default">{{rtableData[0]?rtableData[0].EquipLocalName:'--'}}</div>
-            <div class="pl-10 font-default">{{rtableData[0]?rtableData[0].BuildLocalName:'--'}}-{{rtableData[0]?rtableData[0].FloorLocalName:'--'}}</div>
-            <div class="pl-10 font-default">{{rtableData[0]?rtableData[0].RoomLocalName:'--'}}</div>
+            <div class="singleDir pl-10 font-default">对象实例</div>
+            <div class="pl-10 font-default">{{rtableData[0]?rtableData[0].ObjectLocalName?rtableData[0].ObjectLocalName:'--':'--'}}</div>
+            <div class="pl-10 font-default">{{rtableData[0]?rtableData[0].BuildLocalName:'--'}}-{{rtableData[0]?rtableData[0].FloorLocalName:'--'}}
+            </div>
+            <div class="pl-10 font-default">{{rtableData[0]?rtableData[0].RoomLocalName?rtableData[0].RoomLocalName:'--':'--'}}</div>
           </el-col>
         </el-row>
       </div>
       <div v-show="rtableData.length>1">
-        <div style="margin:10px 0;">有多个设备实例对应了该设备标识,是否一并清除对应关系?</div>
+        <div style="margin:10px 0;">有多个对象实例对应了该对象标识,是否一并清除对应关系?</div>
         <el-row class="bgf5">
           <el-col :span="6">
-            <div class="singleDir pr-10 font-default tr">设备标识</div>
+            <div class="singleDir pr-10 font-default tr">对象标识</div>
             <div class="pr-10 font-default tr">{{ltableData[0]?ltableData[0].EquipmentMark:'--'}}</div>
             <div class="pr-10 font-default tr">{{ltableData[0]?ltableData[0].LocationFlag.toString():'--'}}</div>
             <div class="pr-10 font-default tr">{{ltableData[0]?ltableData[0].Datasource:'--'}}</div>
@@ -40,18 +41,28 @@
           <el-col :span="18">
             <el-table :data="rtableData" style="width: 100%;max-height:400px;" height="calc(100% - 160px)" @selection-change="changeSelection"
               ref="rtable">
-              <el-table-column label="设备实例" class-name="bgfff">
+              <el-table-column label="对象实例" class-name="bgfff">
                 <el-table-column type="selection" class-name="bgf5"></el-table-column>
-                <el-table-column label="设备实例名称" prop="EquipLocalName" class-name="bgf5" show-overflow-tooltip></el-table-column>
-                <el-table-column label="设备实例编码" prop="ObjectID" class-name="bgfff" show-overflow-tooltip></el-table-column>
-                <el-table-column label="所在建筑楼层" class-name="bgfff">
+                <el-table-column v-for="(item,index) in rightTableHeader" :label="item.label" :key="item.prop" :class-name="index>0?'bgfff':'bgf5'">
                   <template slot-scope="scope">
-                    <el-tooltip :content="scope.row.BuildLocalName&&scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:''" placement="top">
-                      <div class="tool-tip">{{scope.row.BuildLocalName&&scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:''}}</div>
-                    </el-tooltip>
+                    <div v-if="item.prop=='BuildLocalName'">
+                      <el-tooltip
+                        :content="scope.row.BuildLocalName&&scope.row.BuildLocalName!='null'?scope.row.FloorLocalName&&scope.row.FloorLocalName!='null'?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''"
+                        placement="top"
+                        :disabled="!(scope.row.BuildLocalName&&scope.row.BuildLocalName!='null'?scope.row.FloorLocalName&&scope.row.FloorLocalName!='null'?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:'')">
+                        <div class="tool-tip">
+                          {{scope.row.BuildLocalName&&scope.row.BuildLocalName!='null'?scope.row.FloorLocalName&&scope.row.FloorLocalName!='null'?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:'--'}}
+                        </div>
+                      </el-tooltip>
+                    </div>
+                    <div v-else>
+                      <el-tooltip :content="scope.row[item.prop]?scope.row[item.prop].toString():''" placement="top"
+                        :disabled="!scope.row[item.prop]">
+                        <div class="tool-tip">{{scope.row[item.prop]?scope.row[item.prop].toString():'--'}}</div>
+                      </el-tooltip>
+                    </div>
                   </template>
                 </el-table-column>
-                <el-table-column label="所在业务空间" prop="RoomLocalName" class-name="bgfff" show-overflow-tooltip></el-table-column>
               </el-table-column>
             </el-table>
           </el-col>
@@ -76,14 +87,45 @@ export default {
       dialogWidth: "20%", //弹窗宽度
       selection: [],
       deleDisabled: true,
-      deleParam: []
+      deleParam: [],
+      allHeaderData: {
+        equip: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象实例编码', prop: "ObjectID" },
+          { label: '所在建筑楼层', prop: "BuildLocalName" },
+          { label: '所在业务空间', prop: "RoomLocalName" },
+        ],
+        parts: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象实例编码', prop: "ObjectID" },
+          { label: '所在建筑楼层', prop: "BuildLocalName" },
+          { label: '所在业务空间', prop: "RoomLocalName" },
+          { label: '所属设备实例', prop: "CascadeEquipLocalName" },
+        ],
+        system: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象实例编码', prop: "ObjectID" },
+        ],
+        space: [
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象实例编码', prop: "ObjectID" },
+          { label: '所在建筑楼层', prop: "BuildLocalName" },
+        ]
+      },
+      rightTableHeader: []
     };
   },
+  props: {
+    typeName: {
+      default: 'equip'
+    }
+  },
   methods: {
     //弹窗显示
     showDialog(row) {
       this.dialogVisible = true;
       this.loading = true;
+      this.rightTableHeader = this.allHeaderData[this.typeName]
       //需先用对象请求接口返回所需对象
       this.getDynamicQueryPrompt(row)
     },
@@ -123,14 +165,14 @@ export default {
         PageNumber: 1,
         PageSize: 500
       }
-      //通过标识查询设备实例
+      //通过标识查询对象实例
       let promise1 = new Promise((resolve, reject) => {
         dynamicQueryPrompt(p1, res => {
           resolve(res)
         })
       })
       let p2 = {
-        Filters: `ObjectID='${row.ObjectID}'`,
+        Filters: `ObjectID='${row.ObjectID}';TypeCode='${row.TypeCode}'`,
         PageNumber: 1,
         PageSize: 500
       }
@@ -205,7 +247,7 @@ export default {
   .tr {
     text-align: right;
   }
-  .tool-tip{
+  .tool-tip {
     white-space: nowrap;
     overflow: hidden;
     text-overflow: ellipsis;

+ 2 - 2
src/components/point/dynamicdata/dictionaryDevice.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <el-form>
-      <el-form-item label="数据字典设备类型">
+      <el-form-item :label="`数据字典${typeName}类型`">
         <el-select v-model="SubTypeName" multiple collapse-tags placeholder="全部" filterable @change="changeType">
           <el-option v-for="item in DynEquipList" :key="item" :label="item" :value="item"></el-option>
         </el-select>
@@ -32,7 +32,7 @@ export default {
   },
   methods: {
     init() {
-      let param = { Related: this.Related, typeName: this.typeName };
+      let param = { Related: this.Related, TypeName: this.typeName };
       //数据字典
       dynamicDynEquipList(param, res => {
         if (res.Result == "success") {

+ 16 - 14
src/components/point/dynamicdata/equipRules.vue

@@ -8,7 +8,7 @@
           <el-form ref="form" :model="form" :inline="true">
             <el-row>
               <div class="query-item special">
-                <el-input placeholder="请输入设备标识关键字" v-model="form.EquipmentMark" @keyup.enter.native="queryTableData">
+                <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>
@@ -27,20 +27,20 @@
           </el-form>
         </el-col>
         <el-col :span="1">
-          <el-tooltip class="item" effect="dark" content="填充设备标识与实例对应规则" placement="left">
+          <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-col>
       </el-row>
     </div>
     <el-row>
-      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">点位表中出现的所有设备</el-col>
-      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">对应物理世界中的设备实例</el-col>
+      <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="{background:'#f7f9fb'}">
-        <el-table-column prop="EquipmentMark" label="设备标识" show-overflow-tooltip min-width="100"></el-table-column>
+      <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">
@@ -49,13 +49,13 @@
           </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="数字字典设备类型" show-overflow-tooltip min-width="100"></el-table-column>
-        <el-table-column prop="EquipLocalName" label="对应的设备实例" show-overflow-tooltip min-width="100" class-name="td-bl"></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="100">
           <template slot-scope="scope">
-            <el-tooltip :content="scope.row.BuildLocalName&&scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:''"
+            <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:''}}
+              <div class="tool-tip">{{scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''}}
               </div>
             </el-tooltip>
           </template>
@@ -101,6 +101,11 @@ export default {
   },
   data() {
     return {
+      headerStyle: {
+        backgroundColor: '#e1e4e5',
+        color: '#2b2b2b',
+        lineHeight: '30px'
+      },
       sourceList: [], //数据源
       DynEquipList: [], //数据字典设备类型
       LocFlagList: [], //位置标签
@@ -243,6 +248,7 @@ export default {
   /deep/ .table-area {
     width: 100%;
     height: calc(100% - 125px);
+    margin-bottom: 10px;
     td div {
       white-space: nowrap;
       text-overflow: ellipsis;
@@ -257,10 +263,6 @@ export default {
       text-overflow: ellipsis;
     }
   }
-  .fr {
-    padding-top: 10px;
-    float: right;
-  }
 }
 </style>
 

+ 70 - 58
src/components/point/dynamicdata/historyDialog.vue

@@ -2,74 +2,42 @@
   <el-dialog title="本次对应记录" :visible.sync="dialogVisible" width="70%" @close="handleClose" id="historyAction" custom-class="act-history">
     <span>提示:可使用Contorl+F 搜索关键字</span>
     <div>
-      <el-table :data="tableData" style="width: 100%;" :show-header="showheader" height="400px">
+      <el-table :data="tableData" style="width: 100%;" :show-header="showheader" height="400px" :cell-style="cellStyle">
         <el-table-column>
-          <el-table-column label="数据源" align="right">
+          <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.Datasource" placement="top">
-                  <div>{{t.Datasource}}</div>
-                </el-tooltip>
-              </div>
-            </template>
-          </el-table-column>
-          <el-table-column label="位置标签" align="right">
-            <template slot-scope="scope">
-              <div v-for="t in scope.row.leftList" :key="t.id">
-                <el-tooltip :content="t.LocationFlag.toString()" placement="top">
-                  <div>{{t.LocationFlag.toString()}}</div>
-                </el-tooltip>
-              </div>
-            </template>
-          </el-table-column>
-          <el-table-column label="设备标识" width="200px" class-name="bgf5" align="right">
-            <template slot-scope="scope">
-              <div v-for="t in scope.row.leftList" :key="t.id">
-                <el-tooltip :content="t.EquipmentMark" placement="top">
-                  <div>{{t.EquipmentMark}}</div>
+                <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>
             </template>
           </el-table-column>
         </el-table-column>
         <el-table-column>
-          <el-table-column label="设备实例名称" width="200px" class-name="bgf5 border-l">
+          <el-table-column v-for="item in showRightHeader" :label="item.label" :key="item.prop">
             <template slot-scope="scope">
               <div v-for="t in scope.row.rightList" :key="t.id">
-                <el-tooltip :content="t.EquipLocalName" placement="top">
-                  <div>{{t.EquipLocalName}}</div>
-                </el-tooltip>
-              </div>
-            </template>
-          </el-table-column>
-          <el-table-column label="设备实例编码">
-            <template slot-scope="scope">
-              <div v-for="t in scope.row.rightList" :key="t.id">
-                <el-tooltip :content="t.ObjectID" placement="top">
-                  <div>{{t.ObjectID}}</div>
-                </el-tooltip>
-              </div>
-            </template>
-          </el-table-column>
-          <el-table-column label="所在建筑楼层">
-            <template slot-scope="scope">
-              <div v-for="t in scope.row.rightList" :key="t.id">
-                <el-tooltip
-                  :content="t.BuildLocalName&&t.FloorLocalName&&t.BuildLocalName!='null'&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:''"
-                  placement="top">
-                  <div>
-                    {{t.BuildLocalName&&t.FloorLocalName&&t.BuildLocalName!='null'&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:''}}
-                  </div>
-                </el-tooltip>
-              </div>
-            </template>
-          </el-table-column>
-          <el-table-column label="所在业务空间">
-            <template slot-scope="scope">
-              <div v-for="t in scope.row.rightList" :key="t.id">
-                <el-tooltip :content="t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:''" placement="top">
-                  <div>{{t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:''}}</div>
-                </el-tooltip>
+                <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:'')">
+                    <div>
+                      {{t.BuildLocalName&&t.BuildLocalName!='null'?t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:t.BuildLocalName:'--'}}
+                    </div>
+                  </el-tooltip>
+                </div>
+                <div v-else-if="item.prop=='RoomLocalName'">
+                  <el-tooltip :content="t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:''" placement="top"
+                    :disabled="!(t.RoomLocalName&&t.RoomLocalName!='null')">
+                    <div>{{t.RoomLocalName&&t.RoomLocalName!='null'?t.RoomLocalName:'--'}}</div>
+                  </el-tooltip>
+                </div>
+                <div v-else>
+                  <el-tooltip :content="t[item.prop]?t[item.prop].toString():''" placement="top" :disabled="!t[item.prop]">
+                    <div>{{t[item.prop]?t[item.prop].toString():'--'}}</div>
+                  </el-tooltip>
+                </div>
               </div>
             </template>
           </el-table-column>
@@ -90,11 +58,47 @@ export default {
     return {
       dialogVisible: false, //弹窗显示与隐藏
       showheader: false,
-      tableData: [] //数据list
+      tableData: [], //数据list
+      showLeftHeader: [
+        { label: '数据源', prop: 'Datasource' },
+        { label: '位置标签', prop: 'LocationFlag' },
+        { label: '设备标识', prop: 'EquipmentMark' }
+      ],
+      tableHeader: {
+        equip: [
+          { label: '对象实例名称', prop: 'ObjectLocalName' },
+          { label: '对象实例编码', prop: 'ObjectID' },
+          { label: '所在建筑楼层', prop: 'BuildLocalName' },
+          { label: '所在业务空间', prop: 'RoomLocalName' },
+        ],
+        parts: [
+          { label: '对象实例名称', prop: 'ObjectLocalName' },
+          { label: '对象实例编码', prop: 'ObjectID' },
+          { label: '对象所属设备', prop: 'CascadeEquipLocalName' },
+          { label: '所在建筑楼层', prop: 'BuildLocalName' },
+          { label: '所在业务空间', prop: 'RoomLocalName' },
+        ],
+        system: [
+          { label: '对象实例名称', prop: 'ObjectLocalName' },
+          { label: '对象实例编码', prop: 'ObjectID' }
+        ],
+        space:[
+          { label: '对象实例名称', prop: "ObjectLocalName" },
+          { label: '对象实例编码', prop: "ObjectID" },
+          { label: '所在建筑楼层', prop: "BuildLocalName" },
+        ]
+      }, //所有类型列表头部
+      showRightHeader: [], //实际显示的列表头部
     };
   },
+  props: {
+    typeName: {
+      default: 'equip'
+    }
+  },
   methods: {
     showDialog(data) {
+      this.showRightHeader = this.tableHeader[this.typeName]
       this.tableData = data;
       this.dialogVisible = true;
     },
@@ -114,6 +118,14 @@ export default {
           this.$emit("delSuc");
         }
       });
+    },
+    //渲染中间两列颜色
+    cellStyle(data) {
+      if (data.columnIndex == 2) {
+        return 'background-color:#f5f7fa'
+      } else if (data.columnIndex == 3) {
+        return 'background-color:#f5f7fa;border-left:2px solid #ccc;'
+      }
     }
   },
   mounted() { },

+ 1 - 1
src/components/point/dynamicdata/locationCascader.vue

@@ -30,7 +30,7 @@ export default {
   },
   methods: {
     init() {
-      let param = { Related: this.Related, typeName: this.typeName };
+      let param = { Related: this.Related, TypeName: this.typeName };
       dynamicLocationList(param, res => {
         this.LocationList = res.Content[0].List;
         //添加未明确对象

+ 1 - 1
src/components/point/dynamicdata/locationFlag.vue

@@ -27,7 +27,7 @@ export default {
   methods: {
     init() {
       this.Datasource = [];
-      let param = { Related: this.Related, typeName: this.typeName };
+      let param = { Related: this.Related, TypeName: this.typeName };
       // 位置标签
       dynamicLocFlagList(param, res => {
         if (res.Result == "success") {

+ 54 - 37
src/components/point/dynamicdata/partsRules.vue

@@ -1,6 +1,5 @@
 
 <template>
-<!-- 部件 -->
   <div id="equipRules">
     <!-- 查询条件 -->
     <div class="query-area">
@@ -8,58 +7,65 @@
         <el-col :span="23">
           <el-form ref="form" :model="form" :inline="true">
             <el-row>
-              <div class="query-item">
-                <el-form-item>
-                  <el-input placeholder="请输入部件标识关键字" v-model="form.EquipmentMark" class="input-with-select">
-                    <el-button slot="append" icon="el-icon-search" @click="queryTableData"></el-button>
-                  </el-input>
-                </el-form-item>
+              <div class="query-item special">
+                <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>数据源</label>
-                <dataSource :Related="true" @change="changeDataSource"></dataSource>
+                <label style="padding-right:9px">数据源</label>
+                <dataSource :Related="null" @change="changeDataSource" :typeName="typeName"></dataSource>
               </div>
               <div class="query-item">
-                <dictionary-device @change="changeDictionary" :Related="true"></dictionary-device>
+                <dictionary-device @change="changeDictionary" :Related="null" :typeName="typeName"></dictionary-device>
               </div>
               <div class="query-item">
-                <label>位置标签</label>
-                <locationFlag :Related="true" @change="changeLocationFlag"></locationFlag>
+                <label style="padding-right:9px">位置标签</label>
+                <locationFlag :Related="null" @change="changeLocationFlag" :typeName="typeName"></locationFlag>
               </div>
             </el-row>
           </el-form>
         </el-col>
         <el-col :span="1">
-          <el-tooltip class="item" effect="dark" content="填充部件标识与实例对应规则" placement="left">
-            <el-button size="small" type="primary" @click="toAddRelation" icon="el-icon-edit-outline" style="float:right;"></el-button>
+          <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-col>
       </el-row>
     </div>
     <el-row>
-      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">点位表中出现的所有部件</el-col>
-      <el-col :span="12" style="padding-left:10px;font-size:13px;border-left:2px solid #000;">对应物理世界中的部件实例</el-col>
+      <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="{background:'#f7f9fb'}">
-        <el-table-column prop="EquipmentMark" label="部件标识" show-overflow-tooltip min-width="100"></el-table-column>
+      <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">
-              <span>{{scope.row.LocationFlag.toString()}}</span>
+              <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="数字字典部件类型" show-overflow-tooltip min-width="100"></el-table-column>
-        <el-table-column prop="EquipLocalName" label="对应的部件实例" show-overflow-tooltip min-width="100" class-name="td-bl"></el-table-column>
-        <el-table-column prop="FloorLocalName" label="实例所在建筑楼层" show-overflow-tooltip min-width="100"></el-table-column>
-        <el-table-column prop="RoomLocalName" label="实例所在业务空间" show-overflow-tooltip min-width="200"></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="100">
+          <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="scope.row.Related=='False'"
+              <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>
@@ -71,7 +77,7 @@
       :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"></del-relation-dialog>
+    <del-relation-dialog ref="del" @refresh="refresh" :typeName="'parts'"></del-relation-dialog>
   </div>
 </template>
 <script>
@@ -96,6 +102,11 @@ export default {
   },
   data() {
     return {
+      headerStyle: {
+        backgroundColor: '#e1e4e5',
+        color: '#2b2b2b',
+        lineHeight: '30px'
+      },
       sourceList: [], //数据源
       DynEquipList: [], //数据字典部件类型
       LocFlagList: [], //位置标签
@@ -123,7 +134,10 @@ export default {
     locationFlag
   },
   props: {
-    typeName: {}
+    typeName: {},
+    isPending: {
+      default: false
+    }
   },
   created() {
     this.init();
@@ -132,7 +146,6 @@ export default {
   methods: {
     //获取查询条件-提示信息
     init() {
-      alert(1)
       this.getTableData()
     },
     //清除对应关系
@@ -151,7 +164,7 @@ export default {
     },
     //跳转至填充对应规则
     toAddRelation() {
-      this.$router.push({ path: "equipRela", query: { typeName: this.typeName } });
+      this.$router.push({ path: "partsRela", query: { typeName: this.typeName } });
     },
     //获取表格数据
     getTableData() {
@@ -177,16 +190,15 @@ export default {
       // 查询对应关系(P1)
       dynamicQuery(param, res => {
         this.loading = false;
-        if (res.Result == "success") {
-          this.tableData = res.Content;
-          this.page.total = res.Total;
-        }
+        this.tableData = res.Content;
+        this.page.total = res.PageSize < 50 ? res.PageSize : res.Total;
       });
     },
     //刷新列表
     refresh() {
       this.page.pageNumber = 1;
-      this.getTypeNames();
+      this.$emit('refresh', this.typeName);
+      this.getTableData();
     },
     //数据字典部件类型修改
     changeDictionary(val) {
@@ -231,9 +243,13 @@ export default {
       margin-left: 10px;
     }
   }
+  /deep/ .special .el-input__inner {
+    width: 220px;
+  }
   /deep/ .table-area {
     width: 100%;
     height: calc(100% - 125px);
+    margin-bottom: 10px;
     td div {
       white-space: nowrap;
       text-overflow: ellipsis;
@@ -242,10 +258,11 @@ export default {
     .td-bl {
       border-left: 1px solid #ebeef5;
     }
-  }
-  .fr {
-    padding-top: 10px;
-    float: right;
+    .tool-tip {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
   }
 }
 </style>

+ 269 - 0
src/components/point/dynamicdata/spaceRules.vue

@@ -0,0 +1,269 @@
+
+<template>
+  <div id="equipRules">
+    <!-- 查询条件 -->
+    <div class="query-area">
+      <el-row>
+        <el-col :span="23">
+          <el-form ref="form" :model="form" :inline="true">
+            <el-row>
+              <div class="query-item special">
+                <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="1">
+          <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-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
+    };
+  },
+  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();
+    }
+  },
+  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% - 125px);
+    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>
+

+ 269 - 0
src/components/point/dynamicdata/systemRules.vue

@@ -0,0 +1,269 @@
+
+<template>
+  <div id="equipRules">
+    <!-- 查询条件 -->
+    <div class="query-area">
+      <el-row>
+        <el-col :span="23">
+          <el-form ref="form" :model="form" :inline="true">
+            <el-row>
+              <div class="query-item special">
+                <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="1">
+          <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-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="400" class-name="td-bl"></el-table-column>
+        <!-- <el-table-column label="所在建筑楼层" min-width="100">
+          <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="'system'"></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
+    };
+  },
+  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: "systemRela", 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();
+    }
+  },
+  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% - 125px);
+    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>
+

+ 33 - 0
src/components/ready/buildfloor/addBuild.vue

@@ -0,0 +1,33 @@
+<template>
+  <el-dialog :title="buildTitle" :visible.sync="buildDialogVis" width="20%" id="messageDialog">
+    <div>
+      <label>建筑名称 : </label>
+      <el-input placeholder="请输入建筑名称" v-model="buildName"></el-input>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button size="small">取消</el-button>
+      <el-button size="small" type="primary" @click="save">确认</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      buildName: '',
+      buildDialogVis: false
+    }
+  },
+  props: {
+    buildTitle: {
+      default: "添加建筑"
+    }
+  },
+  methods: {
+    showDialog() {
+      this.buildDialogVis = true
+    },
+    save() { }
+  }
+}
+</script>

+ 74 - 0
src/components/ready/buildfloor/addConnectivity.vue

@@ -0,0 +1,74 @@
+<template>
+  <el-dialog title="楼层贯通关系维护" :visible.sync="connectDialogVis" width="30%" id="messageDialog">
+    <el-row>
+      <div>添加与{{floor.FloorLocalName}}层有贯通关系的楼层 : </div>
+      <div :span="16">
+        <bfCascader ref="bfCascader" :FloorID="floor.FloorID"></bfCascader>
+      </div>
+    </el-row>
+    <span slot="footer" class="dialog-footer">
+      <el-button size="small">取消</el-button>
+      <el-button size="small" type="primary" @click="save">确认</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+import bfCascader from '@/components/ready/buildfloor/buildfloorCascader'
+import { createRelationInFloor } from "@/api/scan/request";
+export default {
+  data() {
+    return {
+      buildName: '',
+      connectDialogVis: false,
+      floor: {}
+    }
+  },
+  components: {
+    bfCascader
+  },
+  methods: {
+    showDialog() {
+      this.connectDialogVis = true
+      this.$nextTick(() => {
+        this.$refs.bfCascader.getCascader()
+        let arr = this.floor.FloorThroughList || [];
+        let value = []
+        if (arr.length) {
+          arr.map(t => {
+            value.push([t.BuildID, t.FloorID])
+          })
+        }
+        this.$refs.bfCascader.value = value
+      })
+    },
+    save() {
+      let arr = this.$refs.bfCascader.value;
+      let param = {
+        FloorId: this.floor.FloorID,
+        FloorOtherIdList: []
+      }
+      arr.map(t => {
+        param.FloorOtherIdList.push(t[1])
+      })
+      createRelationInFloor(param, res => {
+        this.connectDialogVis = false;
+        this.$message.success('关联成功');
+        this.$emit('refresh')
+      })
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+#messageDialog {
+  .el-row > div {
+    float: left;
+  }
+  .el-row > div + div {
+    margin-left: 10px;
+  }
+  /deep/ .el-input__inner {
+    vertical-align: baseline;
+  }
+}
+</style>

+ 50 - 0
src/components/ready/buildfloor/buildfloorCascader.vue

@@ -0,0 +1,50 @@
+<template>
+  <el-cascader v-model="value" :options="options" :props="props" clearable></el-cascader>
+</template>
+<script>
+import { buildingQuery } from "@/api/scan/request";
+export default {
+  data() {
+    return {
+      value: [],
+      props: {
+        multiple: true,
+        value: 'BuildID',
+        label: 'BuildLocalName',
+        children: 'Floor'
+      },
+      options: []
+    }
+  },
+  props: {
+    FloorID: {
+      default: ''
+    }
+  },
+  created() { },
+  methods: {
+    getCascader() {
+      let param = {
+        Cascade: [
+          { Name: "floor" }
+        ],
+        PageNumber: 1,
+        PageSize: 50
+      }
+      buildingQuery(param, res => {
+        res.Content.map(t => {
+          if (t.Floor && t.Floor.length) {
+            t.Floor = t.Floor.map(item => {
+              if (item.FloorID == this.FloorID) return
+              item.BuildID = item.FloorID
+              item.BuildLocalName = item.FloorLocalName || item.FloorName
+              return item
+            }).filter(it => it)
+          }
+        })
+        this.options = res.Content
+      })
+    },
+  }
+}
+</script>

+ 70 - 0
src/components/ready/buildfloor/formItems.vue

@@ -0,0 +1,70 @@
+<template>
+  <div id="inforsPoint">
+    <el-form ref="ruleForm" :model="form" label-width="150px" :inline="true" :label-position="'left'">
+      <el-form-item :label="item.InfoPointName" v-for="item in InforsList" :key="item.InfoPointCode"
+        :class="{FloorTypeSelect:item.InputMode == 'D1'||item.Unit}">
+        <el-input v-model="form[item.Path]" v-if="item.Visible && (item.InputMode == 'B1')"></el-input>
+        <el-input v-model="form[item.Path]" v-else-if="item.Visible && (item.InputMode == 'A1'||item.InputMode == 'A2')" type="number">
+          <template slot="append" v-if="item.Unit">{{item.Unit}}</template>
+        </el-input>
+        <el-select v-model="form[item.Path]" placeholder="请选择" v-else-if="item.Visible && (item.InputMode == 'D1')">
+          <el-option v-for="op in item.options" :key="op.Code" :label="op.Name" :value="op.Code"></el-option>
+        </el-select>
+        <el-input v-model="form[item.Path]"
+          v-else-if="item.Visible && (item.InputMode == 'L'||item.InputMode == 'L1'||item.InputMode == 'L2'||item.InputMode == 'M')"></el-input>
+        <el-input v-model="form[item.Path]" v-else readonly title="暂不支持"></el-input>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+<script>
+import { getDataDictionary } from "@/api/scan/request";
+export default {
+  props: {
+    type: {
+      default: 'Floor'
+    }
+  },
+  data() {
+    return {
+      form: {},
+      InforsList: []
+    };
+  },
+  created() {
+    this.getInforsPoint()
+  },
+  methods: {
+    getInforsPoint() {
+      let params = {
+        data: {
+          Orders: "sort asc,InfoPointCode asc",
+          PageNumber: 1,
+          PageSize: 50
+        },
+        type: this.type
+      };
+      getDataDictionary(params, res => {
+        this.InforsList = res.Content.map(item => {
+          if (item.InputMode === 'D1') {
+            item.options = JSON.parse(item.DataSource)
+          }
+          return item
+        })
+      })
+    },
+  }
+};
+</script>
+<style lang="scss" scoped>
+#inforsPoint {
+  .el-form-item {
+    /deep/ label.el-form-item__label {
+      font-size: 12px;
+    }
+  }
+  /deep/ .FloorTypeSelect .el-form-item__content {
+    width: 178px;
+  }
+}
+</style>

+ 21 - 0
src/router/system.js

@@ -29,6 +29,9 @@ import objectData from '@/views/point/objectData'
 import integrateReport from '@/views/point/report'
 import dynamicdata from '@/views/point/dynamicdata'
 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 buildTask from '@/views/data_admin/buildTask'
@@ -144,6 +147,24 @@ export default [
                 meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识'}] }
             },
             {
+                path: 'partsRela',
+                name: 'partsRela',
+                component: partsRela,
+                meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识'}] }
+            },
+            {
+                path: 'systemRela',
+                name: 'systemRela',
+                component: systemRela,
+                meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识'}] }
+            },
+            {
+                path: 'spaceRela',
+                name: 'spaceRela',
+                component: spaceRela,
+                meta: { keepAlive: false, breadcrumbs: [{ label: '系统集成', path: '/point/pointsetting' }, { label: '配置动参从点位取值', path: '/point/dynamicdata' }, { label: '处理未对应实例对象标识'}] }
+            },
+            {
                 path: 'objectdata',
                 name: 'objectData',
                 component: objectData,

+ 72 - 0
src/utils/buildfloor/tools.js

@@ -0,0 +1,72 @@
+const tools = {}
+
+/**
+ * @param arr [a,b,c]
+ * 
+ * val value
+ */
+function returnNewObj(data, arr, val) {
+  if (arr.length > 1) {
+    let k = arr.shift()
+    if (!data[k]) {
+      data[k] = {}
+    }
+    returnNewObj(data[k], arr, val)
+  } else {
+    let curk = arr[0]
+    data[curk] = val;
+  }
+}
+
+/**
+ * @param key a
+ * @param val {b:{c:''}}|''|1
+ * @return a.b.c
+ */
+function rKeyStrVal(key, val) {
+  let nk = key,arr = []
+  if (typeof (val) == 'object') {
+    // for(let k in )
+    return nk + '.' + rKeyStrVal()
+  }
+  return nk
+}
+
+/**
+ * @param data Object
+ * @returns 将对象中 key 为a.b.c 转换为a:{b:{c:''}}
+ */
+tools.formatData = (data) => {
+  let newData = JSON.parse(JSON.stringify(data))
+  for (let key in newData) {
+    if (key.indexOf('.') > -1) {
+      let arr = key.split('.')
+      returnNewObj(newData, arr, newData[key])
+      delete newData[key]
+    }
+  }
+  return newData
+}
+
+/**
+ * @param data {a:{b:{c:{}}}}
+ * 
+ * @returns a.b.c
+ * 
+ */
+tools.desFormatData = (data) => {
+  let newData = {}
+  for (let key in data) {
+    if (typeof (data[key]) == 'object') { 
+      for(let childK in data[key]){
+        let nk = `${key}.${childK}`
+        newData[nk] = data[key][childK]
+      }
+    } else {
+      newData[key] = data[key]
+    }
+  }
+  return newData
+}
+
+export default tools

+ 12 - 4
src/utils/hasontableUtils.js

@@ -8,6 +8,14 @@ const handsonUtils = {
      * @return array     经过排序后或者经过搜索后的数组
      */
     getParam: function(changeData, source, hot, trimmedArr) {
+        function arrayUnique(arr, name){ //去重
+            var hash = {};
+            return arr.reduce(function(item, next) {
+                hash[next[name]] ? '' : hash[next[name]] = true && item.push(next);
+                return item;
+            }, []);
+        }
+        let newData = arrayUnique(changeData, 0)
         let param = "";
         //被筛选过后的数组
         // let trimmedArr = this.trimmedRows();
@@ -16,21 +24,21 @@ const handsonUtils = {
         if (trimmedArr.length && isSort) {
             //排序后的数组
             let sortArr = hot.getPlugin("columnSorting").rowsMapper.__arrayMap;
-            param = changeData.map(item => {
+            param = newData.map(item => {
                 return hot.getSourceDataAtRow(trimmedArr[sortArr[item[0]]]);
             });
         } else if (isSort) {
             //排序后的数组
             let sortArr = hot.getPlugin("columnSorting").rowsMapper.__arrayMap;
-            param = changeData.map(item => {
+            param = newData.map(item => {
                 return hot.getSourceDataAtRow(sortArr[item[0]]);
             });
         } else if (trimmedArr.length) {
-            param = changeData.map(item => {
+            param = newData.map(item => {
                 return hot.getSourceDataAtRow(trimmedArr[item[0]]);
             });
         } else {
-            param = changeData.map(item => {
+            param = newData.map(item => {
                 return hot.getSourceDataAtRow(item[0]);
             });
         }

+ 39 - 30
src/views/point/dynamicdata/addRelation/equipRela/index.vue

@@ -5,7 +5,7 @@
       <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;">
-          <dictionary-device @change="changeDictionary" :Related="false"></dictionary-device>
+          <dictionary-device @change="changeDictionary" :Related="false" :typeName="typeName"></dictionary-device>
         </div>
       </el-col>
       <el-col :span="12" class="text-right">
@@ -20,12 +20,12 @@
     <!-- 列表及查询条件区域 -->
     <div class="table-container clearfix">
       <div class="left-table">
-        <h5>未对应的设备标识</h5>
+        <h5>未对应的{{typeName}}标识</h5>
         <!-- 查询条件 -->
         <div class="query-box clearfix">
           <div class="query-item">
-            <label>设备标识关键字</label>
-            <el-input placeholder="请输入设备标识关键字" v-model="form.EquipmentMark" @keyup.enter.native="queryLeftTable">
+            <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>
@@ -53,8 +53,8 @@
                   </el-tooltip>
                 </template>
               </el-table-column>
-              <el-table-column prop="SubTypeName" label="设备类型关键字" align="right" show-overflow-tooltip></el-table-column>
-              <el-table-column prop="EquipmentMark" label="设备标识" show-overflow-tooltip align="right" width="200" class-name="bgf5"></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>
@@ -62,7 +62,7 @@
           <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')">
@@ -79,12 +79,12 @@
         </div>
       </div>
       <div class="right-table" style="float: right;">
-        <h5>未对应的设备实例</h5>
+        <h5>未对应的{{typeName}}实例</h5>
         <!-- 查询条件 -->
         <div class="query-box clearfix">
           <div class="query-item">
-            <label>设备实例关键字</label>
-            <el-input placeholder="请输入设备实例关键字" v-model="form.EquipLocalName" @keyup.enter.native="queryRightTable">
+            <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>
@@ -99,17 +99,17 @@
             <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="设备实例名称" width="200" class-name="bgf5" show-overflow-tooltip>
-                <template slot-scope="scope">{{ scope.row.EquipLocalName }}</template>
+              <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="ObjectID" label="设备实例编码" show-overflow-tooltip></el-table-column>
-              <el-table-column prop="SubTypeName" label="设备类型" show-overflow-tooltip></el-table-column>
+              <el-table-column prop="ObjectID" :label="`${typeName}实例编码`" show-overflow-tooltip></el-table-column>
+              <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.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:''"
+                  <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:''}}</div>
+                      {{scope.row.BuildLocalName?scope.row.FloorLocalName?scope.row.BuildLocalName+'-'+scope.row.FloorLocalName:scope.row.BuildLocalName:''}}</div>
                   </el-tooltip>
                 </template>
               </el-table-column>
@@ -120,7 +120,7 @@
           <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')">
@@ -128,7 +128,7 @@
                   <el-checkbox v-model="t.checked"></el-checkbox>
                   <span>{{(t.Proximity*100).toFixed(2)}}%</span>
                 </p>
-                <p>{{t.EquipLocalName}}</p>
+                <p>{{t.ObjectLocalName}}</p>
                 <p>
                   {{t.BuildLocalName&&t.BuildLocalName!='null'&&t.FloorLocalName&&t.FloorLocalName!='null'?t.BuildLocalName+'-'+t.FloorLocalName:''}}
                 </p>
@@ -179,7 +179,7 @@ export default {
         SubTypeName: [], //数据字典设备类型
         LocationFlag: [], //位置标签
         Datasource: [], //数据源
-        EquipLocalName: "", //设备实例关键字
+        ObjectLocalName: "", //设备实例关键字
         locationVal: [] //所在位置下拉
       }, //查询条件
       leftRecoList: [], //左推荐
@@ -300,22 +300,30 @@ export default {
     },
     //少于20条请求数据-否则删除选中项
     delSelectedRows(lArray, rArray) {
-      for (let i = this.LtableData.length - 1; i >= 0; i--) {
-        for (let j = 0; j < lArray.length; j++) {
-          if (this.LtableData[i].EquipmentMark == lArray[j].EquipmentMark && this.LtableData[i].SubTypeCode == lArray[j].SubTypeCode) {
-            this.LtableData.splice(i, 1)
+      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; //结束当前本轮循环,开始新的一轮循环
           }
         }
       }
-      for (let i = this.RtableData.length - 1; i >= 0; i--) {
-        for (let j = 0; j < rArray.length; j++) {
-          if (this.RtableData[i].ObjectID == rArray[j].ObjectID && this.RtableData[i].SubTypeCode == rArray[j].SubTypeCode) {
-            this.RtableData.splice(i, 1)
+      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()
       }
@@ -385,8 +393,8 @@ export default {
         TypeNameList: [this.typeName]
       };
       //处理查询条件
-      if (this.form.EquipLocalName && this.form.EquipLocalName.length) {
-        param.EquipLocalName = this.form.EquipLocalName;
+      if (this.form.ObjectLocalName && this.form.ObjectLocalName.length) {
+        param.ObjectLocalName = this.form.ObjectLocalName;
       }
       if (this.form.SubTypeName.length) {
         param.SubTypeNameList = this.form.SubTypeName;
@@ -557,7 +565,8 @@ export default {
       h5 {
         line-height: 44px;
         padding-left: 5px;
-        background-color: #dfe6ec;
+        background-color: #e1e4e5;
+        color: #2b2b2b
       }
       .query-box {
         margin-bottom: 20px;

+ 722 - 0
src/views/point/dynamicdata/addRelation/partsRela/index.vue

@@ -0,0 +1,722 @@
+<template>
+  <div id="addRelation">
+    <!-- 数据字典部件类型 -->
+    <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;">
+          <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="ObjectID" :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.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></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="'parts'"></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>
+#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;
+    }
+  }
+}
+</style>
+<style>
+body {
+  overflow-y: hidden;
+}
+</style>

+ 722 - 0
src/views/point/dynamicdata/addRelation/spaceRela/index.vue

@@ -0,0 +1,722 @@
+<template>
+  <div id="addRelation">
+    <!-- 数据字典部件类型 -->
+    <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;">
+          <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="ObjectID" :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.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></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>
+#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;
+    }
+  }
+}
+</style>
+<style>
+body {
+  overflow-y: hidden;
+}
+</style>

+ 722 - 0
src/views/point/dynamicdata/addRelation/systemRela/index.vue

@@ -0,0 +1,722 @@
+<template>
+  <div id="addRelation">
+    <!-- 数据字典系统类型 -->
+    <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;">
+          <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="ObjectID" :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.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></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="'system'"></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: false, //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>
+#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;
+    }
+  }
+}
+</style>
+<style>
+body {
+  overflow-y: hidden;
+}
+</style>

+ 32 - 8
src/views/point/dynamicdata/index.vue

@@ -26,10 +26,18 @@
         </el-tooltip>
       </div>
     </el-row>
-    <equip-rules v-if="curType=='设备'" ref="equip" :typeName="curType" @refresh="equipRefresh" :isPending="isPending"></equip-rules>
-    <!-- <parts-rules v-if="!curType=='设备'" ref="parts" :typeName="'设备'"></parts-rules> -->
+    <!-- 设备 -->
+    <equipRules v-if="curType=='设备'" ref="equip" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"></equipRules>
+    <!-- 部件 -->
+    <partsRules v-else-if="curType=='部件'" ref="parts" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"></partsRules>
+    <!-- 系统 -->
+    <systemRules v-else-if="curType=='系统'" ref="system" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"></systemRules>
+    <!-- 空间 -->
+    <spaceRules v-else-if="curType=='空间'" ref="space" :typeName="curType" @refresh="refreshTabs" :isPending="isPending"></spaceRules>
     <!-- 应用规则弹窗 -->
-    <apply-rules-dialog ref="apply" @refresh="getExecuteFeedback" @cancel="cancelApply"></apply-rules-dialog>
+    <apply-rules-dialog ref="apply" @refresh="getExecuteFeedback" @cancel="cancelApply" @clash="getClash"></apply-rules-dialog>
+    <!-- 冲突日志 -->
+    <applyLog ref="applyLog"></applyLog>
     <!-- 规则更新提示 -->
     <el-dialog title="提示" :visible.sync="ruleDialogShow" width="25%" @close="rulesClose">
       <span>{{tipCon}}</span>
@@ -39,7 +47,7 @@
       </span>
     </el-dialog>
     <!-- 选择增量 or 全量 执行 -->
-    <el-dialog title="提示" :visible.sync="allApplyDialog" width="50%" @close="allApplyClose">
+    <el-dialog title="提示" :visible.sync="allApplyDialog" width="30%" @close="allApplyClose">
       <div id="applySelect">
         <el-radio v-model="force" :label="false">增量执行:只设定对应规则or原始点位有变化的实例动态参数信息点</el-radio>
         <p style="color:#999;">因自动判断是否变化,可能更新不够完整,但速度比较快</p>
@@ -63,9 +71,13 @@
 <script>
 import tools from "@/utils/tools";
 import { mapGetters, mapActions } from "vuex";
-import applyRulesDialog from "@/components/point/dynamicdata/applyRulesDialog";
+// import applyRulesDialog from "@/components/point/dynamicdata/applyRulesDialog";
+import applyRulesDialog from "@/components/point/dynamicdata/applyRulesDialog-copy";
+import applyLog from "@/components/point/dynamicdata/applyLog";
 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 {
   dynamicClashConfirm,
   dynamicPromptmessage,
@@ -89,12 +101,16 @@ export default {
       force: false, //默认增量执行 ----- false - 增量 true - 全量执行
       isPending: false, //是否执行中:执行中禁用所有功能 true即为执行中
       missionID: '', //任务id
+      hasClash: false, //默认不含冲突
     };
   },
   components: {
     applyRulesDialog,
+    applyLog,
     equipRules,
-    partsRules
+    partsRules,
+    systemRules,
+    spaceRules
   },
   created() {
     let typeName = this.$route.query.typeName;
@@ -203,8 +219,8 @@ export default {
       this.allApplyDialog = false;
       this.force = false;
     },
-    //刷新设备类
-    equipRefresh(val) {
+    //刷新
+    refreshTabs(val) {
       this.curType = val;
       this.getTypeNames()
     },
@@ -224,6 +240,10 @@ export default {
             setTimeout(() => {
               this.isPending = false;
               this.getPrompt()
+              this.$refs.apply.getPromptobjs()
+              if(this.hasClash){
+                this.$refs.applyLog.showDialog(missionID)
+              }
             }, 2000)
           } else {
             this.isPending = true;
@@ -239,6 +259,10 @@ export default {
     cancelApply(x) {
       this.isPending = false;
       this.tipsType = 1;
+    },
+    //接收是否有冲突
+    getClash(length){
+      this.hasClash = !!length
     }
   },
   watch: {

+ 106 - 0
src/views/ready/buildfloor/addFloor/index.vue

@@ -0,0 +1,106 @@
+<template>
+  <el-dialog :title="title" :visible.sync="dialogVisible" width="40%" @close="handleClose" id="messageDialog">
+    <div>
+      <formItems :type="'Floor'" ref="formItems"></formItems>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button size="small">取消</el-button>
+      <el-button size="small" type="primary" @click="confirm">确认</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+import {
+  manageCreateFloor,
+  manageUpdateFloor
+} from "@/api/scan/request";
+import formItems from "@/components/ready/buildfloor/formItems";
+import tools from "@/utils/buildfloor/tools";
+export default {
+  props: {
+    title: {
+      default: '提示'
+    },
+    type: {
+      default: 'Floor'
+    },
+    curBuildId: {
+      default: ''
+    },
+    curFloorId: {
+      default: ''
+    }
+  },
+  components: {
+    formItems
+  },
+  data() {
+    return {
+      dialogVisible: false, //弹窗显示与隐藏
+      floorData: {}
+    };
+  },
+  methods: {
+    showDialog(data) {
+      this.floorData = data || {};
+      this.floorData = tools.desFormatData(this.floorData)
+      this.timeoutSetVal()
+      this.dialogVisible = true;
+    },
+    timeoutSetVal() {
+      setTimeout(() => {
+        if (this.$refs.formItems) {
+          this.$refs.formItems.form = this.floorData
+        } else {
+          this.timeoutSetVal()
+        }
+      }, 500)
+    },
+    handleClose() { },
+    //创建
+    confirm() {
+      let form = this.$refs.formItems.form
+      let newform = tools.formatData(form)
+      newform.BuildID = this.curBuildId
+      let Param = {
+        Content: [newform]
+      }
+      if(!newform.FloorLocalName){
+        this.$message.warning('请填写楼层本地名')
+        return
+      }
+      if(!newform.FloorSequenceID){
+        this.$message.warning('请填写楼层顺序号')
+        return
+      }
+      if (newform.FloorID) {
+        manageUpdateFloor(Param, res => {
+          this.$message.success('更新成功')
+          this.$emit('refresh')
+          this.dialogVisible = false;
+        })
+      } else {
+        manageCreateFloor(Param, res => {
+          this.$message.success('创建成功')
+          this.$emit('refresh')
+          this.dialogVisible = false;
+        })
+      }
+    }
+  },
+  mounted() { },
+  created() { }
+};
+</script>
+<style lang="scss" scoped>
+#messageDialog {
+  .el-form-item {
+    /deep/ label.el-form-item__label {
+      font-size: 12px;
+    }
+  }
+  /deep/ .FloorTypeSelect .el-form-item__content {
+    width: 178px;
+  }
+}
+</style>

+ 286 - 28
src/views/ready/buildfloor/index.vue

@@ -1,36 +1,294 @@
 <template>
-    <div>
-      <das-board>
-        <template v-slot:plan>
-          <span>{{plan?plan:"yyyy-mm-dd"}}</span>
-        </template>
-        <template v-slot:finish>
-          <span>{{finish?finish:"yyyy-mm-dd"}}</span>
-        </template>
-        <template v-slot:onLine>
-          <span>{{onLine?onLine:"yyyy-mm-dd"}}</span>
-        </template>
-        <template v-slot:explain>
-          <span>{{explain?explain:"yyyy-mm-dd"}}</span>
-        </template>
-      </das-board>
-    </div>
+  <div id="bd-fl-manage">
+    <el-row>
+      <div class="l-list">
+        <div class="action-box">
+          <div style="height:35px;"></div>
+          <div v-if="0">
+            <el-button size="small" type="default" icon="el-icon-plus" @click="addBuild"></el-button>
+            <el-button size="small" type="default" icon="el-icon-minus" @click="delBuild"></el-button>
+            <el-button size="small" type="default" icon="el-icon-edit-outline" @click="editBuild"></el-button>
+          </div>
+        </div>
+        <h4>建筑</h4>
+        <div class="build-list">
+          <div v-for="(item,index) in buildList" :key="item.BuildID" :class="{'floor-item':true,active:item.active}" @click="changeBuild(index)">
+            <span>
+              {{item.BuildLocalName || item.BuildName}}
+              <el-badge class="mark" :is-dot="false" />
+            </span>
+          </div>
+        </div>
+      </div>
+      <div class="r-table">
+        <div class="action-box">
+          <el-button size="small" type="default" @click="addFloor">添加楼层</el-button>
+        </div>
+        <div class="table-box">
+          <el-table :data="tableData" style="width: 100%" height="100%" v-loading="loading" :header-cell-style="headerStyle">
+            <el-table-column label="楼层本地名">
+              <template slot-scope="scope">{{scope.row.FloorLocalName||scope.row.FloorName}}</template>
+            </el-table-column>
+            <el-table-column label="楼层信息">
+              <template slot-scope="scope">
+                <el-button size="mini" @click="editFloorData(scope.row)" plain icon="el-icon-edit-outline"></el-button>
+              </template>
+            </el-table-column>
+            <el-table-column prop="Datasource" label="平面图">
+              <template slot-scope="scope">
+                <p v-if="scope.row.StructureInfo&&scope.row.StructureInfo.FloorMap">
+                  <i class="iconfont icon-floorplan"></i>
+                  查看平面图
+                </p>
+                <p v-else-if="scope.row.type==2">
+                  <el-badge is-dot>
+                    <i class="iconfont icon-floorplan"></i>
+                  </el-badge>
+                  平面图重复
+                </p>
+                <p v-else>
+                  <i class="iconfont icon-nopicture"></i>
+                  暂无平面图
+                </p>
+              </template>
+            </el-table-column>
+            <el-table-column prop="SubTypeName" label="楼层贯通关系">
+              <template slot-scope="scope">
+                <span style="margin-right:20px">{{scope.row.FloorThroughList?scope.row.FloorThroughList.length:0}}</span>
+                <el-button size="mini" @click="changeConnection(scope.row)" plain icon="el-icon-edit-outline"></el-button>
+              </template>
+            </el-table-column>
+            <el-table-column prop="action" label="操作">
+              <template slot-scope="scope">
+                <el-button size="mini" @click="handleDelete(scope.row)" type="danger" plain icon="el-icon-delete"></el-button>
+              </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>
+      </div>
+    </el-row>
+    <!-- 添加-修改楼层 -->
+    <addFloor :title="floorTitle" ref="addFloorDialog" :curBuildId="curBuildId" :curFloorId="curFloorId" @refresh="refresh"></addFloor>
+    <!-- 添加-修改建筑 -->
+    <addBuild :title="buildTitle" ref="addBuildDialog"></addBuild>
+    <!-- 删除建筑-删除楼层 -->
+    <el-dialog title="提示" :visible.sync="delDialogVis" width="20%" @close="handleClose" id="messageDialog">
+      <div>确定要删除该{{delText}}?</div>
+      <span slot="footer" class="dialog-footer">
+        <el-button size="small" @click="delDialogVis=false">取消</el-button>
+        <el-button size="small" type="primary" @click="confirmDel">确认</el-button>
+      </span>
+    </el-dialog>
+    <!-- 添加贯通关系弹窗 -->
+    <addConnectivity ref="addConnectivity" @refresh="refresh"></addConnectivity>
+  </div>
 </template>
 
 <script>
-import dasBoard from "@/views/dasboard/index";
+import addFloor from "@/views/ready/buildfloor/addFloor/index";
+import addBuild from "@/components/ready/buildfloor/addBuild";
+import addConnectivity from "@/components/ready/buildfloor/addConnectivity";
+import {
+  buildingQuery,
+  floorQuery,
+  manageDeleteFloor
+} from "@/api/scan/request";
 export default {
-    components: {
-      dasBoard
-    },
-    data() {
-        return {
-          plan: "2019-08",
-          finish: "2019-08",
-          onLine: "2019-08",
-          explain: "建筑楼层管理"
+  components: {
+    addFloor,
+    addBuild,
+    addConnectivity
+  },
+  data() {
+    return {
+      floorTitle: '添加楼层',
+      buildTitle: '添加建筑',
+      delDialogVis: false,
+      delText: '建筑',
+      headerStyle: {
+        backgroundColor: '#e1e4e5',
+        color: '#2b2b2b',
+        lineHeight: '30px'
+      },
+      buildList: [],
+      tableData: [
+        {
+          EquipmentMark: 1
         }
+      ],
+      page: {
+        pageSize: 50,
+        pageSizes: [10, 20, 50, 100],
+        pageNumber: 1,
+        total: 0
+      },
+      loading: false, //列表loading
+      curBuildId: '', //当前选中的建筑id
+      curFloorId: '', //当前选中的楼层id
+    }
+  },
+  mounted() { },
+  created() {
+    let bdParam = {
+      PageNumber: 1,
+      PageSize: 500
+    }
+    buildingQuery(bdParam, res => {
+      this.buildList = res.Content;
+      if (this.buildList.length) {
+        this.changeBuild(0)
+      }
+    })
+  },
+  methods: {
+    //change build
+    changeBuild(index) {
+      this.buildList.map(item => {
+        item.active = false
+        return item
+      })
+      this.buildList[index].active = true;
+      this.curBuildId = this.buildList[index].BuildID;
+      this.getFloorTableData()
+      this.$forceUpdate()
     },
-    mounted() {}
+    //add build
+    addBuild() {
+      this.$refs.addBuildDialog.showDialog()
+    },
+    //delete build
+    delBuild() {
+      this.delText = '建筑';
+      this.delDialogVis = true
+    },
+    //edit build
+    editBuild() { },
+    //delete floor
+    handleDelete(floor) {
+      this.delText = '楼层';
+      this.delDialogVis = true;
+      this.curFloorId = floor.FloorID;
+    },
+    //确认删除弹窗关闭
+    handleClose() { },
+    handleSizeChange(pageSize) { },
+    handleCurrentChange(pageNumber) { },
+    addFloor() {
+      this.curFloorId = ''
+      this.$refs.addFloorDialog.showDialog()
+    },
+    // 获取列表
+    getFloorTableData() {
+      let floorParam = {
+        Cascade: [{ Name: 'floorThroughList' }],
+        Orders: "FloorSequenceID desc",
+        PageNumber: this.page.pageNumber,
+        PageSize: this.page.pageSize,
+        Filters: `BuildID='${this.curBuildId}'`
+      }
+      floorQuery(floorParam, res => {
+        this.tableData = res.Content;
+        this.page.total = res.Total
+      })
+    },
+    // 创建楼层成功-修改关系成功
+    refresh() {
+      this.getFloorTableData()
+    },
+    // 确认删除(删除建筑-楼层公用)
+    confirmDel() {
+      if (this.delText == '楼层') {
+        let delParam = [{ FloorID: this.curFloorId }]
+        manageDeleteFloor(delParam, res => {
+          this.$message.success('删除成功');
+          this.delDialogVis = false;
+          this.getFloorTableData()
+        })
+      } else {
+        //todo
+      }
+    },
+    // 修改楼层信息
+    editFloorData(floor) {
+      this.curFloorId = floor.FloorID;
+      this.$refs.addFloorDialog.showDialog(floor)
+    },
+    // 修改楼层贯通关系
+    changeConnection(row) {
+      this.$refs.addConnectivity.showDialog()
+      this.$refs.addConnectivity.floor = row
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+#bd-fl-manage {
+  overflow: hidden;
+  height: 100%;
+  position: relative;
+  .el-row {
+    height: 100%;
+    & > div {
+      float: left;
+      height: 100%;
+      overflow: hidden;
+      background-color: #fff;
+      box-sizing: border-box;
+      border: 1px solid #dfe6ec;
+      .action-box {
+        padding: 10px;
+        .el-button--small {
+          padding: 10px 11px;
+        }
+      }
+    }
+    .l-list {
+      width: 17%;
+      overflow-y: auto;
+      h4 {
+        padding-left: 10px;
+        border-top: 1px solid #dfe6ec;
+        border-bottom: 1px solid #dfe6ec;
+        line-height: 44px;
+      }
+      .build-list {
+        line-height: 48px;
+        .floor-item {
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          span {
+            margin-left: 10px;
+          }
+        }
+        .floor-item.active,
+        .floor-item:hover {
+          background-color: #f5f7fa;
+          color: #000;
+        }
+      }
+    }
+    .r-table {
+      width: 82%;
+      margin-left: 1%;
+      .table-box {
+        height: calc(100% - 106px);
+        margin-bottom: 8px;
+        .iconfont {
+          vertical-align: middle;
+        }
+        /deep/ .el-badge__content.is-fixed {
+          transform: translateY(-6%) translateX(-100%);
+        }
+        p {
+          cursor: pointer;
+        }
+      }
+    }
+  }
 }
-</script>
+</style>