Browse Source

Merge branch 'tenant' into dev

haojianlong 5 years ago
parent
commit
521b82ec20

+ 1 - 1
package.json

@@ -28,7 +28,7 @@
         "@saga-web/base": "2.1.9",
         "@saga-web/draw": "2.1.61",
         "@saga-web/graphy": "2.1.34",
-        "@saga-web/cad-engine": "2.0.420"
+        "@saga-web/cad-engine": "2.0.423"
     },
     "devDependencies": {
         "ajv": "^6.9.1",

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

@@ -846,6 +846,12 @@ export function getRentTableData(param, success) {
 }
 
 //创建租户信息
+export function queryRentTableData(param, success) {
+    let url = `${baseUrl}/datacenter/tenant/query`
+    http.postJson(url, param, success)
+}
+
+//创建租户信息
 export function createRentTableData(param, success) {
     let url = `${baseUrl}/datacenter/tenant/create`
     http.postJson(url, param, success)
@@ -863,6 +869,42 @@ export function deleteRentTableData(param, success) {
     http.postJson(url, param, success)
 }
 
+//租户关联租户业务空间,租户一对多,覆盖
+export function tnRelateTenantZone(param, success) {
+    let url = `${baseUrl}/datacenter/tn-in-sp-zone-Tenant/link-sp`
+    http.postJson(url, param, success)
+}
+
+//租户关联默认业务空间,租户一对多,覆盖
+export function tnRelateGeneralZone(param, success) {
+    let url = `${baseUrl}/datacenter/tn-in-sp-zone-general/link-sp`
+    http.postJson(url, param, success)
+}
+
+//租户关联默认业务空间,租户一对多,覆盖
+export function tnRelateZone(param, success) {
+    let url = `${baseUrl}/datacenter/tn-in-sp-base/link-sp?type=${param.type}`
+    http.postJson(url, param.data, success)
+}
+
+//租户关联业务空间,累加
+export function tnRelateOneZone(param, success) {
+    let url = `${baseUrl}/datacenter/tn-in-sp-base/link?type=${param.type}`
+    http.postJson(url, param.data, success)
+}
+
+//删除租户业务空间关系
+export function delTnRelateZone(param, success) {
+    let url = `${baseUrl}/datacenter/tn-in-sp-base/unlink?type=${param.type}`
+    http.postJson(url, param.data, success)
+}
+
+//查询租户关联的业务空间所在的建筑楼层
+export function queryTnRelateFloor(param, success) {
+    let url = `${baseUrl}/equip-component/tenant/tenant-building-floor?objectType=${param.objectType}&tenantId=${param.tenantId}`
+    http.postJson(url, {}, success)
+}
+
 //关联分区空间
 export function saveRentRelateSpace(param, success) {
     let url = `${baseUrl}/datacenter/tenant/relate-space`
@@ -1360,6 +1402,12 @@ export function countZone(param, success) {
     http.postJson(url, data, success)
 }
 
+//查询空间信息父类
+export function queryParentZone(param, success) {
+    let url = `${baseUrl}/datacenter/zonespacebase/query`;
+    http.postJson(url, param, success)
+}
+
 //动态数据关联-查询对应关系(删除提示页)
 export function dynamicQueryPrompt(param, success) {
     let url = `${baseUrl}/pointconfig/dynamic/query-prompt`;

BIN
src/assets/image/floorBG.png


+ 334 - 0
src/components/ledger/rentList/roomCountDrawer.vue

@@ -0,0 +1,334 @@
+<template>
+  <el-drawer title="关联的业务空间" :visible.sync="drawer" :direction="direction" :before-close="handleClose" size="20%" custom-class="room-count">
+    <div class="drawer-body" :style="{height:drawerBodyHeight}">
+      <div class="default-zone zone">
+        <p>默认业务空间</p>
+        <div v-if="!(readonly&&!zoneList.GeneralZone.length)">
+          <div v-for="(item,index) in zoneList.GeneralZone" :key="item.RoomID" class="zoneItem">
+            {{item.BuildLocalName}}-{{item.FloorLocalName}}-{{item.RoomLocalName || item.RoomName}}
+            <i size="mini" class="el-icon-delete" title="删除" @click="deleteZoneItem('GeneralZone',index)" v-if="!readonly"></i>
+          </div>
+          <div class="zoneItem" style="border:none;padding:0;" v-if="!readonly">
+            <el-cascader placeholder="请选择" v-model="GeneralVal" :options="option.GeneralZone" :props="props" filterable @expand-change="expandChange"
+              @change="change" style="width:100%;">
+            </el-cascader>
+          </div>
+        </div>
+        <div v-else class="center">
+          <i class="icon-wushuju iconfont"></i>
+          暂无数据
+        </div>
+      </div>
+      <div class="tenant-zone zone">
+        <p>租户业务空间</p>
+        <div v-if="!(readonly&&!zoneList.TenantZone.length)">
+          <div v-for="(item,index) in zoneList.TenantZone" :key="item.RoomID" class="zoneItem">
+            {{item.BuildLocalName}}-{{item.FloorLocalName}}-{{item.RoomLocalName || item.RoomName}}
+            <i size="mini" class="el-icon-delete" title="删除" @click="deleteZoneItem('TenantZone',index)" v-if="!readonly"></i>
+          </div>
+          <div class="zoneItem" style="border:none;padding:0;" v-if="!readonly">
+            <el-cascader placeholder="请选择" v-model="TenantVal" :options="option.TenantZone" :props="props" filterable
+              @expand-change="expandChangeTenantZone" @change="changeTenantZone" style="width:100%;">
+            </el-cascader>
+          </div>
+        </div>
+        <div v-else class="center">
+          <i class="icon-wushuju iconfont"></i>
+          暂无数据
+        </div>
+      </div>
+    </div>
+    <div class="btn" v-if="!readonly">
+      <el-button type="primary" @click="save">保存</el-button>
+    </div>
+  </el-drawer>
+</template>
+<script>
+import {
+  tnRelateTenantZone, //关联租户空间
+  tnRelateGeneralZone, //关联默认空间
+  tnRelateOneZone,// 关联业务空间
+  delTnRelateZone, // 删除关系业务空间与租户
+  buildingQuery, //建筑查询
+  queryParentZone, //业务空间查询GeneralZone-TenantZone
+} from '@/api/scan/request'
+import { mapGetters } from "vuex";
+export default {
+  data() {
+    return {
+      drawer: false,
+      direction: 'rtl',
+      zoneList: {
+        GeneralZone: [],
+        TenantZone: []
+      },
+      option: {
+        GeneralZone: [],
+        TenantZone: []
+      },
+      props: {
+        children: 'Floor'
+      },
+      GeneralVal: [],
+      TenantVal: [],
+      curTenantId: '',
+      drawerBodyHeight: ''
+    }
+  },
+  props: {
+    readonly: {
+      default: true
+    }
+  },
+  computed: {
+    ...mapGetters("layout", ["projectId"])
+  },
+  watch: {
+    projectId() {
+      this.init();
+    }
+  },
+  mounted() {
+    this.drawerBodyHeight = document.body.clientHeight - 152 + 'px'
+  },
+  created() {
+    this.init();
+  },
+  methods: {
+    init() {
+      this.getBuilding()
+    },
+    getBuilding() {
+      let pa = {
+        Cascade: [{ Name: 'floor', Orders: 'FloorSequenceID desc' }],
+        Order: 'BuildLocalName asc'
+      }
+      buildingQuery(pa, res => {
+        this.option.GeneralZone = res.Content.map(build => {
+          build.value = build.BuildID;
+          build.label = build.BuildLocalName;
+          build.Floor = build.Floor.map(floor => {
+            floor.value = floor.FloorID;
+            floor.label = floor.FloorLocalName;
+            floor.Floor = [];
+            return floor;
+          });
+          build.Floor.unshift({ value: 'noknowFloor', label: '未明确楼层', Floor: [] })
+          return build;
+        })
+        this.option.GeneralZone.unshift({ value: 'noknowBuild', label: '未明确建筑', Floor: [] });
+        this.option.TenantZone = JSON.parse(JSON.stringify(this.option.GeneralZone));
+      })
+    },
+    expandChange(val) {
+      this.middleware(val, "GeneralZone")
+    },
+    expandChangeTenantZone(val) {
+      this.middleware(val, "TenantZone")
+    },
+    middleware(val, type) {
+      let filters = '';
+      if (val[0] == 'noknowBuild') { //未明确建筑
+        filters = `buildingId isNull`
+        this.getZone(filters, type, val)
+      } else if (val[1]) {
+        if (val[1] == "noknowFloor") {
+          filters = `buildingId='${val[0]}';floorId isNull`
+        } else {
+          filters = `buildingId='${val[0]}';floorId='${val[1]}'`
+        }
+        this.getZone(filters, type, val)
+      }
+    },
+    getZone(filters, type, selected) {
+      if (!filters) return;
+      let params = {
+        Cascade: [{ Name: 'building' }, { Name: 'floor' }],
+        Filters: `${filters};ObjectType='${type}'`,
+        Orders: "RoomID asc",
+        PageSize: 1000,
+        Projection: ["RoomID", "RoomLocalName", "RoomName", "ObjectType", "BuildingId", "FloorId"]
+      }
+      queryParentZone(params, res => {
+        if (res.Content.length) {
+          let tempArr = res.Content.map(t => {
+            t.BuildLocalName = t.Building ? t.Building.BuildLocalName : '未明确建筑';
+            t.FloorLocalName = t.Floor ? t.Floor.FloorLocalName : '未明确楼层';
+            delete t.Floor;
+            t.value = JSON.parse(JSON.stringify(t));
+            t.label = t.RoomLocalName || t.RoomName;
+            return t;
+          })
+          if (selected.length == 1) {
+            this.option[type][0].Floor = tempArr;
+          } else {
+            for (let i = 0; i < this.option[type].length; i++) {
+              if (this.option[type][i].value == selected[0]) {
+                for (let j = 0; j < this.option[type][i].Floor.length; j++) {
+                  if (this.option[type][i].Floor[j].value == selected[1]) {
+                    this.option[type][i].Floor[j].Floor = tempArr;
+                  }
+                }
+              }
+            }
+          }
+        }
+      })
+    },
+    change(val) {
+      let zoneObj = val[val.length - 1];
+      if (zoneObj.BuildLocalName == '未明确建筑') {
+        zoneObj.FloorLocalName = ''
+      }
+      if (this.zoneList.GeneralZone.findIndex((item) => (item.RoomID == zoneObj.RoomID)) == -1) {
+        this.zoneList.GeneralZone.push(zoneObj);
+        this.relateGeneral('GeneralZone', zoneObj.RoomID)
+      } else {
+        this.$message.warning('已有业务空间,请重新选择')
+      }
+      this.GeneralVal = [];
+    },
+    changeTenantZone(val) {
+      let zoneObj = val[val.length - 1];
+      if (zoneObj.BuildLocalName == '未明确建筑') {
+        zoneObj.FloorLocalName = ''
+      }
+      if (this.zoneList.TenantZone.findIndex((item) => (item.RoomID == zoneObj.RoomID)) == -1) {
+        this.zoneList.TenantZone.push(zoneObj);
+        this.relateGeneral('TenantZone', zoneObj.RoomID)
+      } else {
+        this.$message.warning('已有业务空间,请重新选择')
+      }
+      this.TenantVal = [];
+    },
+    showDrawer(tenant) {
+      this.curTenantId = tenant.TenantID;
+      if (tenant.ZoneGeneraltList) {
+        this.zoneList.GeneralZone = tenant.ZoneGeneraltList.map(t => {
+          if (t.Building) {
+            t.BuildLocalName = t.Building.BuildLocalName || t.Building.BuildName;
+          } else {
+            t.BuildLocalName = '未明确建筑'
+          }
+          if (t.Floor) {
+            t.FloorLocalName = t.Floor.FloorLocalName || t.Floor.FloorName;
+          } else {
+            t.FloorLocalName = '未明确楼层'
+          }
+          if (t.BuildLocalName == '未明确建筑') {
+            t.FloorLocalName = ''
+          }
+          return t;
+        });
+      } else {
+        this.zoneList.GeneralZone = [];
+      }
+      if (tenant.ZoneTenantList) {
+        this.zoneList.TenantZone = tenant.ZoneTenantList.map(t => {
+          if (t.Building) {
+            t.BuildLocalName = t.Building.BuildLocalName || t.Building.BuildName;
+          } else {
+            t.BuildLocalName = '未明确建筑'
+          }
+          if (t.Floor) {
+            t.FloorLocalName = t.Floor.FloorLocalName || t.Floor.FloorName;
+          } else {
+            t.FloorLocalName = '未明确楼层'
+          }
+          if (t.BuildLocalName == '未明确建筑') {
+            t.FloorLocalName = ''
+          }
+          return t;
+        });
+      } else {
+        this.zoneList.TenantZone = [];
+      }
+      this.drawer = true;
+    },
+    handleClose(done) {
+      this.zoneList.GeneralZone = [];
+      this.zoneList.GeneralZone = [];
+      this.$emit('refresh');
+      done();
+    },
+    // 删除
+    deleteZoneItem(type, index) {
+      let pa = {
+        type: type,
+        data: [
+          {
+            SpaceID: this.zoneList[type][index].RoomID,
+            TenantID: this.curTenantId
+          }
+        ]
+      }
+      this.zoneList[type].splice(index, 1);
+      delTnRelateZone(pa, res => { })
+    },
+    // 关联默认空间
+    relateGeneral(type, SpaceId) {
+      let pa = {
+        type: type,
+        data: {
+          Content: [
+            {
+              SpaceId: SpaceId,
+              TenantID: this.curTenantId
+            }
+          ]
+        }
+      }
+      tnRelateOneZone(pa, res => { })
+    },
+    save() {
+      this.$emit('refresh');
+      this.drawer = false;
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.room-count {
+  .drawer-body {
+    position: relative;
+    padding: 10px 10px 30px;
+    overflow-y: auto;
+    .zone {
+      padding: 6px 14px 22px;
+      box-shadow: 0 0 3px #cacaca;
+      p {
+        margin-bottom: 4px;
+      }
+      .zoneItem {
+        position: relative;
+        padding: 2px 34px 2px 10px;
+        border: 1px solid #ccc;
+        border-radius: 4px;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        i {
+          position: absolute;
+          right: 10px;
+          top: 50%;
+          transform: translateY(-50%);
+          cursor: pointer;
+        }
+      }
+      .zoneItem + .zoneItem {
+        margin-top: 6px;
+      }
+    }
+    .zone + .zone {
+      margin-top: 10px;
+    }
+  }
+  .btn {
+    position: absolute;
+    width: calc(100% - 20px);
+    bottom: 20px;
+    right: 20px;
+    text-align: right;
+  }
+}
+</style>

+ 7 - 0
src/router/system.js

@@ -69,6 +69,7 @@ import cenotelist from '@/views/ledger/cenotelist' //竖井清单
 import cenoteadd from '@/views/ledger/cenotelist/cenoteadd' //添加竖井
 import cenoteDetail from '@/views/ledger/cenotelist/cenoteDetail' //竖井关系详情
 import rentlist from '@/views/ledger/rentlist' //租户清单
+import manageTenantZone from '@/views/ledger/rentlist/manageTenantZone' //租户清单
 import rentadd from '@/views/ledger/rentlist/rentadd' //租户清单
 import dataReport from '@/views/ledger/report'
 import auth from '@/views/system/auth'
@@ -457,6 +458,12 @@ export default [{
                 meta: { keepAlive: false, breadcrumbs: [{ label: '台账管理', path: '/ledger/facility' }, { label: '租户台账', path: '/ledger/rentlist' }, { label: '添加租户' }] }
             },
             {
+                path: 'manageTenantZone',
+                name: 'manageTenantZone',
+                component: manageTenantZone,
+                meta: { keepAlive: false, breadcrumbs: [{ label: '台账管理', path: '/ledger/facility' }, { label: '租户台账', path: '/ledger/rentlist' }, { label: '管理租赁空间' }] }
+            },
+            {
                 path: 'datareport',
                 name: 'dataReport',
                 component: dataReport,

+ 17 - 15
src/views/ledger/cenotelist/relatedSpace.vue

@@ -7,7 +7,7 @@
           <el-button v-if="isMyTab == 2" size="small" style="float:right" @click="dialogVisible = true">添加</el-button>
           <div v-else>
             <el-button v-show="!isEdit" @click="isEdit = true" size="small" style="float:right">编辑</el-button>
-            <el-button v-show="isEdit" size="small" @click="saveEdit" style="float:right">保存</el-button>
+            <el-button v-show="isEdit" size="small" type="primary" @click="saveEdit" style="float:right">保存</el-button>
             <el-button v-show="isEdit" size="small" @click="cancelEdit" style="float:right">取消</el-button>
           </div>
           <addSpaceDialog :dialogVisible.sync="dialogVisible" ref="addSpaceDialog" @refresh="refresh" :params="cenoteObj" :spaceType="spaceType"
@@ -34,20 +34,22 @@
             <el-option v-for="item in buildingList" :key="item.value" :label="item.BuildLocalName" :value="item.BuildID"></el-option>
           </el-select>
         </el-badge>
-        <div style="height:calc(100% - 42px);margin:10px 0 0 0;width:100%;position:relative;">
-          <el-scrollbar style="height:100%;width:148px;border:1px solid rgb(201,201,201);margin:0px;float:left;">
-            <div class="floor" style="width:149px;height:100%;">
-              <span class="floor-item" style="border-bottom:1px solid rgb(201,201,201)">楼 层</span>
-              <div v-if="building" v-loading="buildingLoading">
-                <span class="floor-item" @click="changeFloor(item.FloorID)" :class="{floorItemChoose:(item.FloorID == floor)}"
-                  v-for="item in buildingObj.Floor" :key="item.FloorID">{{ item.FloorLocalName }}</span>
+        <el-row style="height:calc(100% - 42px);margin:10px 0 0 0;width:100%;position:relative;">
+          <el-col :span="3" style="height:100%;border:1px solid #e4e4e4;">
+            <el-scrollbar style="height:100%;">
+              <div class="floor" style="height:100%;text-align:center;">
+                <span class="floor-item" style="border-bottom:1px solid #e4e4e4;">楼 层</span>
+                <div v-if="building" v-loading="buildingLoading">
+                  <span class="floor-item" @click="changeFloor(item.FloorID)" :class="{floorItemChoose:(item.FloorID == floor)}"
+                    v-for="item in buildingObj.Floor" :key="item.FloorID">{{ item.FloorLocalName }}</span>
+                </div>
               </div>
-            </div>
-          </el-scrollbar>
-          <div style="float:right;height:100%;width:calc(100% - 151px);border:1px solid rgb(201,201,201);border-left:none;">
+            </el-scrollbar>
+          </el-col>
+          <el-col :span="21" style="height:100%;border:1px solid #e4e4e4;border-left:none;">
             <cenote-graphy ref="cenotegraphy"></cenote-graphy>
-          </div>
-        </div>
+          </el-col>
+        </el-row>
 
       </div>
       <div v-show="isMyTab == 2" class="data-item">
@@ -312,7 +314,7 @@ export default {
   background: #fff;
   .header {
     padding-bottom: 10px;
-    border-bottom: 1px solid rgba(0, 0, 0, 0.5);
+    border-bottom: 1px solid #e4e4e4;
     span {
       line-height: 33px;
       margin-left: 15px;
@@ -389,7 +391,7 @@ export default {
   cursor: pointer;
 }
 .floorItemChoose {
-  background: rgba(0, 0, 0, 0.2);
+  background: #e4e4e4;
 }
 /deep/ .el-scrollbar__wrap {
   overflow-x: hidden;

+ 73 - 82
src/views/ledger/rentlist/index.vue

@@ -6,19 +6,11 @@
     <el-row class="left">
       <span style="float:right;">当前筛选条件下共{{page.total || '--'}}租户</span>
       <el-select v-model="onlyRead" @change="getTableHeader" style="width:100px;margin-right:10px;vertical-align:bottom;">
-        <el-option
-          v-for="item in options"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value">
+        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
         </el-option>
       </el-select>
       <el-select v-model="showType" @change="initTable" style="width:100px;margin-right:10px;vertical-align:bottom;">
-        <el-option
-          v-for="item in showTypes"
-          :key="item.value"
-          :label="item.label"
-          :value="item.value">
+        <el-option v-for="item in showTypes" :key="item.value" :label="item.label" :value="item.value">
         </el-option>
       </el-select>
       <!-- <div v-show="onlyRead" style="width:200px;display:inline-block;text-align:left;color:gray;font-size:12px;">
@@ -27,10 +19,11 @@
       </div> -->
       <el-button @click="handleCreateTableData">添加租户</el-button>
       <el-button @click="getTableHeader">刷新</el-button>
+      <el-button @click="mangeTenantZone">管理租赁空间</el-button>
       <el-button v-show="!onlyRead" @click="undo">撤销</el-button>
     </el-row>
     <div class="tableBox">
-      <div class="center middle_sty" style="flex:2;" v-show="tableData && !tableData.length">
+      <div class="center middle_sty" style="flex:1;" v-show="tableData && !tableData.length">
         <p>
           <i class="icon-wushuju iconfont"></i>
           暂无数据
@@ -39,34 +32,9 @@
       <div class="tableLeft" v-show="tableData && tableData.length">
         <handson-table ref="table"></handson-table>
       </div>
-      <div class="tableRight">
-        <div v-show="isTableRightShow">
-          <div class="table_right_box"><span class="iconfont close_right" @click="handleCloseRight">&#xe66b;</span></div>
-          <h5>管理租赁空间</h5>
-          <p style="margin-bottom: 10px;">以下为关联的业务空间id</p>
-          <el-input
-            type="textarea"
-            :autosize="{ minRows: 6, maxRows: 10}"
-            :disabled="onlyRead"
-            placeholder="请输入业务空间id,以“,”分割"
-            v-model="roomId">
-          </el-input>
-          <el-row class="right" v-show="!onlyRead">
-            <el-button @click="handleCloseRight">取消</el-button>
-            <el-button type="primary" @click="saveSpaceList">保存</el-button>
-          </el-row>
-        </div>
-      </div>
     </div>
-    <el-pagination
-      class="right"
-      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"
+    <el-pagination class="right" 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>
     <!-- 不支持的输入方式 -->
@@ -82,6 +50,8 @@
         <el-button type="primary" @click="handleClickUpdate">确 认</el-button>
       </span>
     </el-dialog>
+    <!-- 右侧关联的业务空间抽屉 -->
+    <roomCountDrawer ref="roomCountDrawer" :readonly="onlyRead" @refresh="getTableData"></roomCountDrawer>
   </div>
 </template>
 <script>
@@ -89,15 +59,16 @@ import tools from "@/utils/scan/tools"
 import handsonUtils from "@/utils/hasontableUtils"
 import showTools from "@/utils/handsontable/notShow"
 import text from "@/utils/handsontable/mainText"
-
+import roomCountDrawer from "@/components/ledger/rentList/roomCountDrawer"
 import floorCascader from "@/components/ledger/lib/floorCascader"
 import handsonTable from "@/components/common/handsontable"
-import { getDataDictionary, getRentTableHeader, getRentTableData, updataRentTableData, deleteRentTableData, saveRentRelateSpace, BeatchQueryParam } from "@/api/scan/request"
+import { getDataDictionary, getRentTableHeader, queryRentTableData, updataRentTableData, deleteRentTableData, saveRentRelateSpace, BeatchQueryParam } from "@/api/scan/request"
 import { mapGetters, mapActions } from "vuex";
 export default {
   components: {
     floorCascader,
-    handsonTable
+    handsonTable,
+    roomCountDrawer
   },
   data() {
     return {
@@ -137,35 +108,31 @@ export default {
     };
   },
   computed: {
-    ...mapGetters("layout", [
-        "projectId",
-        "secret",
-        "userId"
-    ]),
-    showTypes () {
-      return this.onlyRead?
-        [{value: "Visible", label: '只看采集信息'}, {value: "all", label: '全部'}]:
-        [{value: "Visible", label: '只看采集信息'}, {value: "all", label: '全部'}]
+    ...mapGetters("layout", ["projectId", "secret", "userId"]),
+    showTypes() {
+      return this.onlyRead ?
+        [{ value: "Visible", label: '只看采集信息' }, { value: "all", label: '全部' }] :
+        [{ value: "partInfo", label: '隐藏信息点' }, { value: "all", label: '全部' }, { value: "Visible", label: '只看采集信息' }]
     }
   },
   created() {
     this.getTableHeader()
   },
   watch: {
-      projectId() {
-        this.getTableHeader()
+    projectId() {
+      this.getTableHeader()
+    },
+    showTypes: {
+      handler(newName, oldName) {
+        if (newName && newName[0] && newName[0].value) {
+          this.showType = newName[0].value
+        } else {
+          this.showType = ""
+        }
       },
-      showTypes: {
-        handler(newName, oldName) {
-          if (newName && newName[0] && newName[0].value) {
-            this.showType = newName[0].value
-          } else {
-            this.showType = ""
-          }
-        },
-        immediate: true,
-        deep: true
-      }
+      immediate: true,
+      deep: true
+    }
   },
   methods: {
     // 获取表头数据(初始化表格)
@@ -181,7 +148,7 @@ export default {
       await getDataDictionary(param, res => {
         this.tableHeader = res.Content;
         this.tableHeader.forEach(item => {
-          if(item.Path && item.InputMode){
+          if (item.Path && item.InputMode) {
             this.inputMap[item.Path] = item.InputMode
           }
         })
@@ -190,16 +157,20 @@ export default {
     },
     async getTableData() {//获取表格数据
       let params = {
+        Cascade: [
+          { Name: 'zoneTenantList', Cascade: [{ Name: 'building' }, { Name: 'floor' }] },
+          { Name: 'zoneGeneraltList', Cascade: [{ Name: 'building' }, { Name: 'floor' }] }
+        ],
         Orders: "createTime desc, TenantID asc",
         PageNumber: this.page.pageNumber,
         PageSize: this.page.pageSize
       }
-      await getRentTableData(params, (res) => {
+      await queryRentTableData(params, (res) => {
         this.tableData = res.Content
         this.copyTableData = tools.deepCopy(res.Content)
         this.page.pageNumber = res.PageNumber
         this.page.total = res.Total
-        if(this.tableData && this.tableData.length){
+        if (this.tableData && this.tableData.length) {
           if (this.onlyRead) {
             this.getBatch(res.Content)
           }
@@ -213,7 +184,7 @@ export default {
         Content: [],
         Projection: []
       }, keyList = [];
-       //生成要修改字段列表
+      //生成要修改字段列表
       change.map(item => {
         let key = item[1].split(".")[0]
         if (item[1] && keyList.indexOf(key) == -1) {
@@ -271,7 +242,7 @@ export default {
       }
       let param = []
       params.map(item => {
-        param.push({TenantID: item.TenantID})
+        param.push({ TenantID: item.TenantID })
       })
       this.$confirm("此操作将删除租户,是否继续?", "提示", {
         confirmButtonText: '确定',
@@ -339,7 +310,7 @@ export default {
     formatHeaderData(list) {//格式化表头显示的数据
       let arr = tools.copyArr(list)
       let data = showTools.headerTextFilter(arr, "tenant", this.onlyRead, this.showType)
-      data.unshift("操作","所属楼层","租赁空间");
+      data.unshift("操作", "所属楼层", "租赁空间");
       return data;
     },
     formatHeaderType(list) {//格式化表头头映射的数据
@@ -354,7 +325,21 @@ export default {
         readOnly: true
       }, {
         data: "RoomCount",
-        renderer: tools.num,
+        renderer: (instance, td, row, col, prop, value, cellProperties) => {
+          td.style.color = "#409EFF";
+          td.style.cursor = "pointer";
+          let num = 0;
+          let a = instance.getDataAtRowProp(row, 'ZoneGeneraltList')
+          let b = instance.getDataAtRowProp(row, 'ZoneTenantList')
+          if (a && a.length) {
+            num += a.length
+          }
+          if (b && b.length) {
+            num += b.length
+          }
+          td.innerHTML = num;
+          return td;
+        },
         readOnly: true
       })
       return data;
@@ -400,12 +385,13 @@ export default {
       }
       //点击关联的元空间
       if (val === "RoomCount") {
-        if(infos && infos.RoomID) {
-          this.roomId = infos.RoomID
-        } else {
-          this.roomId = ""
-        }
-        this.isTableRightShow = true;
+        // if (infos && infos.RoomID) {
+        //   this.roomId = infos.RoomID
+        // } else {
+        //   this.roomId = ""
+        // }
+        // this.isTableRightShow = true;
+        this.$refs.roomCountDrawer.showDrawer(infos)
         return false
       }
       if (!this.onlyRead && showTools.InputModeArr.indexOf(inputMode) == '-1') {
@@ -421,7 +407,7 @@ export default {
       this.updateInput = ''
     },
     //更新临时维护信息点
-    handleClickUpdate(){
+    handleClickUpdate() {
       tools.setDataForKey(this.tableData[this.row], this.updateInfoPoint, this.updateInput)
       this.handleUpdataTable([[this.row, this.updateInfoPoint, null, this.updateInput]], "edit")
       this.updateInputShow = false
@@ -535,7 +521,7 @@ export default {
                         item,
                         head.Path,
                         // child.error ? "表号功能号格式错误" : "表号功能号不存在"
-                        child.error ? child.value? "表号功能号格式错误" : "表号功能号不存在" : "暂未采集到实时数据"
+                        child.error ? child.value ? "表号功能号格式错误" : "表号功能号不存在" : "暂未采集到实时数据"
                       );
                     }
                   });
@@ -547,6 +533,10 @@ export default {
           this.tableExample.loadData(this.tableData);
         });
       }
+    },
+    // 跳转至管理租赁空间
+    mangeTenantZone() {
+      this.$router.push({ name: 'manageTenantZone', query: { onlyRead: this.onlyRead } })
     }
   }
 };
@@ -563,7 +553,7 @@ export default {
   }
   .search-header {
     overflow: hidden;
-    padding:0 10px 10px 10px;
+    padding: 0 10px 10px 10px;
     border-bottom: 1px solid #bcbcbc;
   }
   .tableBox {
@@ -581,7 +571,7 @@ export default {
       padding: 5px 15px;
       margin-right: 5px;
       box-shadow: 0px 1px 5px 0px rgba(59, 66, 84, 0.15);
-      .table_right_box::after{
+      .table_right_box::after {
         display: block;
         content: "";
         clear: both;
@@ -593,7 +583,8 @@ export default {
     }
   }
 }
-.el-pagination button, .el-pagination span:not([class*=suffix]) {
-    vertical-align: middle;
+.el-pagination button,
+.el-pagination span:not([class*="suffix"]) {
+  vertical-align: middle;
 }
 </style>

+ 634 - 0
src/views/ledger/rentlist/manageTenantZone.vue

@@ -0,0 +1,634 @@
+<template>
+  <div id="manageTenantZone">
+    <!-- 数据字典设备类型 -->
+    <el-row>
+      <el-button size="small" type="default" icon="el-icon-back" @click="goback"></el-button>
+      <div style="display:inline-block;margin-left:10px;">
+        <label style="margin-right:10px;">业务空间类型</label>
+        <el-select v-model="value" placeholder="请选择" @change="getTnRelatFloor">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
+          </el-option>
+        </el-select>
+      </div>
+    </el-row>
+    <el-row class="main-container">
+      <el-col :span="3" class="main-left">
+        <div style="padding:10px;border-bottom:1px solid #e4e4e4;">
+          <el-input :placeholder="`请输入租户名称`" @keyup.enter.native="getTenant" v-model="keyWord">
+            <i slot="suffix" class="el-input__icon el-icon-search" @click="getTenant"></i>
+          </el-input>
+        </div>
+        <div style="overflow-y:auto;height: calc(100% - 53px);">
+          <div v-for="(item,i) in tenantList" :key="item.TenantID" :class="{'tenantItem':true, 'active':item.active}" @click="tenantChange(i)">
+            {{item.TenantLocalName||item.TenantName}}
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="21" style="height:100%;">
+        <div style="height:52px;border-bottom:1px solid #e4e4e4;">
+          <div v-if="onlyRead===true||onlyRead==='true'">
+            <div v-for="(bd,i) in allBuilding" :key="bd.BuildID" :class="{'building':true,'active':bd.active}" @click="changeBuildSelect(i)">
+              {{bd.BuildLocalName||bd.BuildName}}
+            </div>
+          </div>
+          <div v-else style="line-height: 52px;padding:0 10px;">
+            <label style="margin-right:10px;">所属建筑</label>
+            <el-select v-model="buildSelect" placeholder="请选择" @change="changeBuildSelect">
+              <el-option v-for="(item,i) in allBuilding" :key="item.BuildID" :label="item.BuildLocalName || item.BuildName" :value="i">
+              </el-option>
+            </el-select>
+            <div style="float:right;">
+              <el-button size="small" type="default" @click="editCanvas" v-if="!isEdit">编辑</el-button>
+              <el-button size="small" type="default" @click="cancel" v-if="isEdit">取消</el-button>
+              <el-button size="small" type="primary" @click="save" v-if="isEdit">确认</el-button>
+            </div>
+          </div>
+        </div>
+        <div class="canvas-container" ref="graphy" v-loading="canvasLoading">
+          <div class="floor-container" v-show="hasMap">
+            <div class="before" @click="toBefore">
+              <div><i class="el-icon-caret-top"></i></div>
+            </div>
+            <div class="floor">
+              <div v-for="(fl,i) in floorList" :key="fl.id" :class="{'active':fl.active,'textParent':true}" @click="floorChange(i)">
+                <div class="text">
+                  {{fl.FloorLocalID || fl.FloorLocalName}}
+                  <span v-if="fl.Count">{{fl.Count>9?'9+':fl.Count}}</span>
+                </div>
+              </div>
+            </div>
+            <div class="after" @click="toNext">
+              <div><i class="el-icon-caret-bottom"></i></div>
+            </div>
+            <div class="border"></div>
+          </div>
+          <canvas id="floorCanvas" :width="canvasWidth" :height="canvasHeight" v-show="hasMap"></canvas>
+          <div class="canvas-fun" v-show="hasMap">
+            <canvasFun @scale="scale" @fit="fit" @savePng="savePng" @saveSvg="saveSvg" @saveJson="saveJson" :config="config"
+              @groupSelect="groupSelect" ref="canvasFun">
+            </canvasFun>
+          </div>
+          <div class="center" v-show="!hasMap">
+            <i class="icon-wushuju iconfont"></i>
+            暂无数据
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script>
+import canvasFun from "@/components/business_space/newGraphy/canvasFun";
+import { mapGetters, mapActions } from "vuex";
+import { SColor, SPoint } from "@saga-web/draw/lib";
+import { DivideFloorScene, SpaceItem, ZoneItem, FloorView } from "@saga-web/cad-engine/lib";
+import { colorArr } from "@/utils/spaceColor"
+import { buildingQuery, queryTnRelateFloor, queryRentTableData, tnRelateZone, queryZone } from '@/api/scan/request'
+import { resolve } from 'q';
+export default {
+  data() {
+    return {
+      options: [{ value: 'GeneralZone', label: '默认分区' }, { value: 'TenantZone', label: '租户分区' }],
+      value: 'TenantZone',
+      tenantList: [],
+      floorList: [],
+      canvasWidth: 800,
+      canvasHeight: 800,
+      onlyRead: false,
+      allBuilding: [],
+      buildSelect: 0,
+      activeFloor: 0,
+      activeTentanIndex: 0,
+      canvasLoading: true,
+      isEdit: false,
+      view: '',
+      scene: '',
+      config: {
+        isEdit: false,
+        groupSelect: true
+      },
+      hasMap: true,
+      keyWord: '', // 查询租户名称
+      Cascade: {
+        GeneralZone: 'zoneGeneraltList',
+        TenantZone: 'zoneTenantList'
+      }
+    }
+  },
+  components: {
+    canvasFun
+  },
+  computed: {
+    ...mapGetters("layout", ["projectId"])
+  },
+  created() {
+    this.onlyRead = this.$route.query.onlyRead;
+    this.init();
+  },
+  mounted() {
+    this.canvasWidth = this.$refs.graphy.offsetWidth;
+    this.canvasHeight = this.$refs.graphy.offsetHeight;
+  },
+  methods: {
+    init() {
+      this.getTenant();
+    },
+    // 返回
+    goback() {
+      this.$router.push({ name: 'rentlist' })
+    },
+    // 适配底图到窗口
+    fit() {
+      this.view.fitSceneToView()
+    },
+    // 缩放
+    scale(val) {
+      if (!this.view) {
+        return;
+      }
+      let scale = this.view.scale;
+      this.view.scaleByPoint(val / scale, this.canvasWidth / 2, this.canvasHeight / 2);
+    },
+    // 保存为png
+    savePng() {
+      this.view.saveImage(`${this.floorList[this.activeFloor].FloorID}.png`, 'png');
+    },
+    // 保存为svg
+    saveSvg() {
+      this.view.saveSceneSvg(`${this.floorList[this.activeFloor].FloorID}.svg`, 6400, 4800);
+    },
+    // 保存json
+    saveJson() {
+      this.view.saveFloorJson(`${this.floorList[this.activeFloor].FloorID}.json`)
+    },
+    // 框选
+    groupSelect() {
+      this.scene.isRectSelection = 2;
+    },
+    // 上一个
+    toBefore() {
+      if (this.activeFloor > 0) {
+        let index = this.activeFloor - 1;
+        this.floorChange(index)
+      }
+    },
+    // 下一个
+    toNext() {
+      if (this.activeFloor < this.floorList.length - 1) {
+        let index = this.activeFloor + 1;
+        this.floorChange(index)
+      }
+    },
+    // 建筑修改
+    changeBuildSelect(val) {
+      this.allBuilding.map(t => {
+        t.active = false;
+      })
+      this.allBuilding[val].active = true;
+      if (this.allBuilding[val].Floor && this.allBuilding[val].Floor.length) {
+        this.floorList = this.allBuilding[val].Floor
+        this.floorChange(0);
+      } else {
+        this.floorList = []
+      }
+    },
+    // 租户修改
+    tenantChange(index) {
+      this.tenantList.map(t => {
+        t.active = false;
+      })
+      this.curTenantId = this.tenantList[index].TenantID;
+      this.tenantList[index].active = true;
+      this.activeTentanIndex = index;
+      this.tenantChangeInflu(index);
+    },
+    // 租户更改引起的修改
+    tenantChangeInflu(i) {
+      if (this.onlyRead === true || this.onlyRead === 'true') { // 只读模式-对空间类型处理
+        let g = this.tenantList[i].ZoneGeneraltList;
+        let t = this.tenantList[i].ZoneTenantList;
+        let gFlag = g && g.length;
+        let tFlag = t && t.length;
+        if (gFlag) {
+          if (tFlag) {
+            this.options = [{ value: 'GeneralZone', label: '默认分区' }, { value: 'TenantZone', label: '租户分区' }]
+            this.value = 'TenantZone';
+          } else {
+            this.options = [{ value: 'GeneralZone', label: '默认分区' }];
+            this.value = 'GeneralZone';
+          }
+        } else {
+          if (tFlag) {
+            this.options = [{ value: 'TenantZone', label: '租户分区' }];
+            this.value = 'TenantZone';
+          } else {
+            this.options = [];
+            this.value = '';
+          }
+        }
+      }
+      this.getTnRelatFloor();
+    },
+    // 楼层修改
+    floorChange(index) {
+      this.floorList.map(t => {
+        t.active = false;
+      })
+      this.activeFloor = index;
+      this.floorList[index].active = true;
+      this.getGraphy(index)
+    },
+    // canvas点击事件
+    canvasClick(item, event) {
+      if (!this.isEdit) {
+        item.selected = false;
+      }
+    },
+    // 编辑
+    editCanvas() {
+      this.isEdit = true;
+      this.config.isEdit = true;
+      this.scene.zoneList.map(t => {
+        t.selected = t.highLightFlag;
+        t.highLightFlag = false;
+      })
+    },
+    // 取消
+    cancel() {
+      this.isEdit = false;
+      this.config.isEdit = false;
+      this.scene.clearZoneSelection();
+    },
+    // 清除canvas
+    clearGraphy() {
+      if (this.view) {
+        this.view.scene = null;
+        return
+      }
+      this.view = new FloorView('floorCanvas')
+    },
+    // 获取列表
+    getTenant() {
+      let pa = {
+        Cascade: [
+          { Name: 'zoneTenantList' },
+          // , Cascade: [{ Name: 'building' }, { Name: 'floor' }] 
+          { Name: 'zoneGeneraltList' }
+        ],
+        Orders: "TenantID asc",
+        PageSize: 1000
+      }
+      if (this.keyWord) {
+        pa.Filters = `TenantLocalName contain '${this.keyWord}'`
+      }
+      queryRentTableData(pa, res => {
+        this.tenantList = res.Content.map(t => {
+          t.active = false;
+          return t;
+        })
+        if (this.tenantList.length) {
+          this.tenantChange(0)
+        }
+      })
+    },
+    // 获取业务空间
+    getBusinessSpace() {
+      let buildid = this.floorList[this.activeFloor].BuildID;
+      let floorid = this.floorList[this.activeFloor].FloorID;
+      let TenantID = this.tenantList[this.activeTentanIndex].TenantID
+      if (buildid && floorid && this.value) {
+        this.canvasLoading = true
+        let pa = {
+          zone: this.value,
+          data: {
+            Filters: `BuildingId='${buildid}';FloorId='${floorid}'`,
+            Orders: "createTime desc, RoomID asc",
+            PageSize: 1000
+          }
+        }
+        // queryZone(pa, res => {
+        //   // 所有业务空间
+        //   this.businessSpaceList = res.Content;
+        //   // 已关联元空间的业务空间
+        //   this.BSPRelaISPList = [];
+        //   res.Content.map(t => {
+        //     if (t.Outline && t.Outline.length) {
+        //       this.BSPRelaISPList.push(t)
+        //     }
+        //   })
+        //   let key = 'Z' + this.Cascade[this.value].substring(1)
+        //   let relationList = this.tenantList[this.activeTentanIndex][key] || []
+        //   // 绘制业务空间
+        //   let tempArr = this.BSPRelaISPList.map((t, i) => {
+        //     if (t.FloorId == this.floorList[this.activeFloor].FloorID && t.ObjectType == this.value) {
+        //       return {
+        //         RoomLocalName: t.RoomLocalName,
+        //         OutLine: t.Outline,
+        //         RoomID: t.RoomID,
+        //         Color: colorArr[i % colorArr.length],
+        //         HighLightFlag: relationList.findIndex((item) => (item.RoomID == t.RoomID)) > -1
+        //       }
+        //     } else {
+        //       return undefined;
+        //     }
+        //   }).filter(item => item)
+        //   this.scene.removeAllZone();
+        //   this.scene.addZoneList(tempArr);
+        //   this.scene.click(this, this.canvasClick);
+        // })
+        this.canvasLoading = false;
+        let promise1 = new Promise((resolve) => {
+          queryZone(pa, res => {
+            resolve(res);
+          })
+        })
+        let pa2 = {
+          Cascade: [
+            { Name: this.Cascade[this.value], Filters: `FloorId='${floorid}'` }
+          ],
+          Filters: `TenantID='${TenantID}'`
+        }
+        let promise2 = new Promise((resolve) => {
+          queryRentTableData(pa2, res => {
+            resolve(res);
+          })
+        })
+        Promise.all([promise1, promise2]).then(response => {
+          let res1 = response[0];
+          let res2 = response[1];
+          let key = 'Z' + this.Cascade[this.value].substring(1);
+          let relationList = res2.Content[0][key] || [];
+          this.businessSpaceList = res1.Content;
+          // 已关联元空间的业务空间
+          this.BSPRelaISPList = [];
+          res1.Content.map(t => {
+            if (t.Outline && t.Outline.length) {
+              this.BSPRelaISPList.push(t)
+            }
+          })
+          // 绘制业务空间
+          let tempArr = this.BSPRelaISPList.map((t, i) => {
+            if (t.FloorId == this.floorList[this.activeFloor].FloorID && t.ObjectType == this.value) {
+              return {
+                RoomLocalName: t.RoomLocalName,
+                OutLine: t.Outline,
+                RoomID: t.RoomID,
+                Color: colorArr[i % colorArr.length],
+                HighLightFlag: relationList.findIndex((item) => (item.RoomID == t.RoomID)) > -1
+              }
+            } else {
+              return undefined;
+            }
+          }).filter(item => item)
+          this.scene.removeAllZone();
+          this.scene.addZoneList(tempArr);
+          this.scene.click(this, this.canvasClick);
+        })
+      }
+    },
+    // 初始化底图
+    getGraphy(index) {
+      if (this.floorList[index].StructureInfo && this.floorList[index].StructureInfo.FloorMap) {
+        let floorMap = this.floorList[index].StructureInfo.FloorMap;
+        this.clearGraphy();
+        this.scene = new DivideFloorScene();
+        this.hasMap = true;
+        this.canvasLoading = true;
+        this.scene.loadUrl(`/image-service/common/file_get?systemId=revit&key=${floorMap}`).then(res => {
+          if (res == 'error') {
+            this.$message.warning('数据解析异常');
+            this.hasMap = false;
+            this.canvasLoading = false;
+            return;
+          }
+          this.view.scene = this.scene;
+          this.scene.isSpaceSelectable = false;
+          // 绘制业务空间
+          this.getBusinessSpace();
+          this.view.fitSceneToView();
+          this.view.minScale = this.view.scale;
+          if (this.$refs.canvasFun) {
+            this.$refs.canvasFun.everyScale = this.view.scale;
+          }
+        })
+      } else {
+        this.hasMap = false;
+      }
+    },
+    // 查询租户关联的业务空间所在建筑楼层,分区修改也会触发
+    getTnRelatFloor() {
+      if (this.value) {
+        let pa = {
+          objectType: this.value,
+          tenantId: this.curTenantId
+        }
+        queryTnRelateFloor(pa, res => {
+          this.allBuilding = res.Content.map(t => {
+            t.active = false;
+            if (t.Floor) {
+              t.Floor.map(item => {
+                item.active = false;
+              })
+            }
+            return t;
+          });
+          if (this.onlyRead === true || this.onlyRead === 'true') {
+            this.allBuilding = this.allBuilding.filter(item => item.Count > 0)
+            this.allBuilding.map(t => {
+              if (t.Floor) {
+                t.Floor = t.Floor.filter(item => item.Count > 0)
+              }
+            })
+          }
+          if (this.allBuilding.length) {
+            this.changeBuildSelect(this.buildSelect);
+          }
+        })
+      } else {
+        this.hasMap = false;
+        this.canvasLoading = false;
+        this.allBuilding = [];
+      }
+    },
+    // 保存关系
+    save() {
+      let arr = this.scene.getSelectedZone();
+      let pa = {
+        data: {
+          SpaceIdList: [],
+          FloorId: this.floorList[this.activeFloor].FloorID,
+          TenantId: this.curTenantId
+        },
+        type: this.value
+      }
+      pa.data.SpaceIdList = arr.map(t => {
+        return t.data.RoomID;
+      })
+      tnRelateZone(pa, res => {
+        this.isEdit = false;
+        this.config.isEdit = false;
+        this.$message.success('关联成功');
+        this.getTnRelatFloor();
+      })
+    },
+  },
+  watch: {
+    projectId() {
+      this.init()
+    },
+    "view.scale": {
+      handler(n) {
+        if (this.$refs.canvasFun) {
+          let s = n * 10 / this.view.minScale
+          this.$refs.canvasFun.sliderVal = s > 1000 ? 1000 : s;
+        }
+      }
+    },
+    "scene.isRectSelection": {
+      handler(n) {
+        if (!n) {
+          this.$refs.canvasFun.active = '';
+        }
+      }
+    },
+  }
+}
+</script>
+<style lang="less" scoped>
+@normal-border-color : #e4e4e4;
+@normal-border-active-color : #e8e8e8;
+#manageTenantZone {
+  height: 100%;
+  .main-container {
+    margin-top: 10px;
+    background: #fff;
+    border: 1px solid @normal-border-color;
+    height: calc(100% - 43px);
+    overflow: hidden;
+    .main-left {
+      height: 100%;
+      border-right: 1px solid @normal-border-color;
+      .tenantItem {
+        border-bottom: 1px solid @normal-border-color;
+        text-align: center;
+        line-height: 40px;
+        color: #2b2b2b;
+        cursor: pointer;
+        &.active {
+          background-color: @normal-border-active-color;
+        }
+      }
+    }
+  }
+  .building {
+    display: inline-block;
+    margin: 7px 10px;
+    border: 1px solid @normal-border-color;
+    color: #2b2b2b;
+    border-radius: 28px;
+    padding: 4px 16px;
+    &.active {
+      border: 1px solid #409eff;
+      color: #409eff;
+    }
+  }
+  .canvas-container {
+    position: relative;
+    width: 100%;
+    height: calc(100% - 53px);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    .floor-container {
+      position: absolute;
+      width: 60px;
+      height: 260px;
+      top: 20px;
+      left: 20px;
+      z-index: 2;
+      background-color: #fff;
+      .before,
+      .after {
+        div {
+          width: 52px;
+          height: 23px;
+          margin: 0 auto;
+          text-align: center;
+          font-size: 24px;
+          line-height: 1;
+          color: #2b2b2b;
+          border: 1px solid @normal-border-color;
+          cursor: pointer;
+        }
+      }
+      .floor {
+        position: relative;
+        height: 210px;
+        overflow-y: auto;
+        &::-webkit-scrollbar {
+          display: none;
+        }
+        .textParent {
+          cursor: pointer;
+          &.active {
+            background-image: url(~@/assets/image/floorBG.png);
+            div.text {
+              border: none;
+            }
+          }
+          div.text {
+            position: relative;
+            width: 54px;
+            height: 30px;
+            line-height: 30px;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            text-align: center;
+            color: #2b2b2b;
+            margin: 0 auto;
+            padding: 0 10px;
+            box-sizing: border-box;
+            &:hover {
+              background-color: #409eff1a;
+            }
+            span {
+              position: absolute;
+              display: block;
+              height: 10px;
+              border-radius: 14px;
+              line-height: 10px;
+              text-align: center;
+              top: 0;
+              right: -2px;
+              color: #fff;
+              background-color: #f56c6c;
+              padding: 5px;
+              font-size: 12px;
+              transform: scale(0.7, 0.7);
+            }
+          }
+        }
+      }
+      div.border {
+        position: absolute;
+        width: 52px;
+        height: 100%;
+        left: 50%;
+        top: 0;
+        z-index: -1;
+        transform: translateX(-50%);
+        border-left: 1px solid @normal-border-color;
+        border-right: 1px solid @normal-border-color;
+      }
+    }
+    .canvas-fun {
+      position: absolute;
+      bottom: 20px;
+      left: 50%;
+      z-index: 3;
+      transform: translateX(-50%;);
+    }
+  }
+}
+</style>