|
@@ -0,0 +1,535 @@
|
|
|
+<template>
|
|
|
+ <div class="operation-plan">
|
|
|
+ <div id="fabricContainer" class="fabric-container">
|
|
|
+ <div id="canvas-container">
|
|
|
+ <canvas id="myCanvas"></canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="left-button">
|
|
|
+ <div :class="'menu-item pangmenzhengdao' + (node.id == pathNode.id ? ' active' :'')"
|
|
|
+ v-for="(node,index) in titleList" :key="node.id" @click="selectItem(node)">
|
|
|
+ {{node.name}}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div id="distribution-drap" class="distribution-drap" v-show="drapShow" @mousedown="dragStart"
|
|
|
+ @touchstart="dragStart">
|
|
|
+ <div class="distribution-drap-title">
|
|
|
+ <div id="title" class="distribution-drap-title-content">{{detail.title}}</div>
|
|
|
+ <i id="close" class="el-icon-circle-close" @click="drapShow = false"></i>
|
|
|
+ </div>
|
|
|
+ <div class="distribution-drap-content" v-if="drapShow">
|
|
|
+ <organization v-if="type === 'organizationDetail'" :detail="detail" @clickName="clickName">
|
|
|
+ </organization>
|
|
|
+ <house v-else-if="type === 'roomDetail'" :detail="detail"></house>
|
|
|
+ <device v-else-if="type === 'monitor' || type === 'lighting' || type === 'airConditioning'"
|
|
|
+ :detail="detail" :type="type" @clickName="clickName">
|
|
|
+ </device>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-dialog :close-on-click-modal="false" :title="noticeBoardType === '1' ? '企业看板':'设备看板'"
|
|
|
+ :visible.sync="modelVisible" width="1100px" :append-to-body="true">
|
|
|
+ <notice-board v-if="modelVisible" :type="noticeBoardType" :detailId="detail.id"></notice-board>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import {
|
|
|
+ updateProjectItemTarget,
|
|
|
+ getHouseListByPage
|
|
|
+ } from '@/httpApi/space'
|
|
|
+ import {
|
|
|
+ getOrganizationListByPage
|
|
|
+ } from '@/httpApi/business'
|
|
|
+ import * as fabric from 'fabric'
|
|
|
+ import noticeBoard from '@/components/work/common/noticeBoard'
|
|
|
+ import organization from '@/components/work/bim/modelDetail/organization'
|
|
|
+ import house from '@/components/work/bim/modelDetail/house'
|
|
|
+ import device from '@/components/work/bim/modelDetail/device'
|
|
|
+ export default {
|
|
|
+ props: ['target'],
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ canvas: null,
|
|
|
+ backgroundImageUrl: '',
|
|
|
+ roomList: [],
|
|
|
+ floorPlanSize: {
|
|
|
+ width: '',
|
|
|
+ height: ''
|
|
|
+ },
|
|
|
+ detail: {},
|
|
|
+ noticeBoardType: '1',
|
|
|
+ modelVisible: false,
|
|
|
+ drapShow: false,
|
|
|
+ type: '',
|
|
|
+ organizationObj: {},
|
|
|
+ titleList: [{
|
|
|
+ id: 1,
|
|
|
+ name: '企业'
|
|
|
+ }, {
|
|
|
+ id: 2,
|
|
|
+ name: '监控',
|
|
|
+ type: 'monitor'
|
|
|
+ }, {
|
|
|
+ id: 3,
|
|
|
+ name: '照明',
|
|
|
+ type: 'lighting'
|
|
|
+ }, {
|
|
|
+ id: 4,
|
|
|
+ name: '空调',
|
|
|
+ type: 'airConditioning'
|
|
|
+ }],
|
|
|
+ pathNode: {
|
|
|
+ id: 1,
|
|
|
+ name: '企业'
|
|
|
+ },
|
|
|
+ signList: [{
|
|
|
+ id: 1,
|
|
|
+ name: '摄像头',
|
|
|
+ iconPath: './assets/shexiangtou.png'
|
|
|
+ }, {
|
|
|
+ id: 2,
|
|
|
+ name: '门禁',
|
|
|
+ iconPath: './assets/menjin.png'
|
|
|
+ }, {
|
|
|
+ id: 3,
|
|
|
+ name: '停车',
|
|
|
+ iconPath: './assets/car.png'
|
|
|
+ }, {
|
|
|
+ id: 4,
|
|
|
+ name: '照明',
|
|
|
+ iconPath: './assets/zhaoming.png'
|
|
|
+ }]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ window.removeEventListener('resize', this.initResize);
|
|
|
+ },
|
|
|
+ components: {
|
|
|
+ noticeBoard,
|
|
|
+ organization,
|
|
|
+ house,
|
|
|
+ device
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ if (!this.target.floorPlan) return;
|
|
|
+ let width = document.getElementById('fabricContainer').clientWidth;
|
|
|
+ document.getElementById('distribution-drap').style.left = (width - 330) + 'px';
|
|
|
+ document.getElementById('distribution-drap').style.top = '30px';
|
|
|
+ getOrganizationListByPage({
|
|
|
+ currPage: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ organizationId: this.$store.getters.organization.id,
|
|
|
+ projectId: this.$store.getters.project.id
|
|
|
+ }).then(res => {
|
|
|
+ if (res.state) {
|
|
|
+ let list = res.data.dataList,
|
|
|
+ obj = {};
|
|
|
+ for (let i = 0; i < list.length; i++) {
|
|
|
+ if (list[i].roomIds) {
|
|
|
+ let roomIds = list[i].roomIds.split(',');
|
|
|
+ for (let k = 0; k < roomIds.length; k++) {
|
|
|
+ obj[roomIds[k]] = list[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.organizationObj = obj;
|
|
|
+ this.init();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ window.addEventListener('resize', this.initResize);
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ init() {
|
|
|
+ this.backgroundImageUrl = JSON.parse(this.target.floorPlan)[0].url;
|
|
|
+ if (this.target.floorPlanSize) {
|
|
|
+ this.floorPlanSize = JSON.parse(this.target.floorPlanSize);
|
|
|
+ this.createCanvas();
|
|
|
+ } else {
|
|
|
+ this.initSize();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ initSize() {
|
|
|
+ let box = document.getElementById('fabricContainer');
|
|
|
+ if (!box) return;
|
|
|
+ let boxWidth = box.offsetWidth,
|
|
|
+ boxHeight = box.offsetHeight;
|
|
|
+ let imgObj = new Image();
|
|
|
+ imgObj.src = this.backgroundImageUrl;
|
|
|
+ imgObj.onload = () => {
|
|
|
+ let imgWidth = imgObj.width,
|
|
|
+ imgHeight = imgObj.height;
|
|
|
+ let scale = imgWidth / imgHeight;
|
|
|
+ this.floorPlanSize.width = boxWidth >= boxHeight ? boxHeight * scale : boxWidth;
|
|
|
+ this.floorPlanSize.height = boxWidth < boxHeight ? boxWidth / scale : boxHeight;
|
|
|
+ updateProjectItemTarget({
|
|
|
+ id: this.target.projectItemTargetId,
|
|
|
+ floorPlanSize: JSON.stringify(this.floorPlanSize)
|
|
|
+ })
|
|
|
+ this.createCanvas();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ clickName(noticeBoardType) {
|
|
|
+ this.modelVisible = true;
|
|
|
+ this.noticeBoardType = noticeBoardType;
|
|
|
+ },
|
|
|
+ createCanvas() {
|
|
|
+ this.canvas = new fabric.Canvas('myCanvas', {
|
|
|
+ fireRightClick: true, // 启用右键,button的数字为3
|
|
|
+ stopContextMenu: true, // 禁止默认右键菜单
|
|
|
+ controlsAboveOverlay: true, // 超出clipPath后仍然展示控制条
|
|
|
+ preserveObjectStacking: true, // 当选择画布中的对象时,让对象不在顶层。
|
|
|
+ width: this.floorPlanSize.width, // 设置画布宽度
|
|
|
+ height: this.floorPlanSize.height, // 设置画布高度
|
|
|
+ });
|
|
|
+ document.getElementById('canvas-container').style.width = this.floorPlanSize.width + 'px';
|
|
|
+ document.getElementById('canvas-container').style.hight = this.floorPlanSize.height + 'px';
|
|
|
+ this.initBackgroundImage();
|
|
|
+ this.clickFunction();
|
|
|
+ },
|
|
|
+ initResize() {
|
|
|
+ if (!this.canvas) return;
|
|
|
+ let box = document.getElementById('fabricContainer');
|
|
|
+ if (!box) return;
|
|
|
+ let boxWidth = box.offsetWidth,
|
|
|
+ container = document.getElementById('myCanvas').offsetWidth;
|
|
|
+ let scale = boxWidth / container >= 1 ? 1 : boxWidth / container;
|
|
|
+ document.getElementById('canvas-container').style.transform = 'scale(' + scale + ')';
|
|
|
+ },
|
|
|
+ clickFunction() {
|
|
|
+ this.canvas.on('mouse:down', options => {
|
|
|
+ if (!options.target.detailType) return;
|
|
|
+ this.showDrap(options.target.detailType, options.target.detailData)
|
|
|
+ });
|
|
|
+ },
|
|
|
+ dragStart(evt) {
|
|
|
+ let oEvent = evt || event; //获取事件对象,这个是兼容写法
|
|
|
+ if (oEvent.target.id !== 'title') return;
|
|
|
+ let div = document.getElementById('distribution-drap');
|
|
|
+ let disX = oEvent.clientX - parseInt(div.style.left || 0);
|
|
|
+ let disY = oEvent.clientY - parseInt(div.style.top || 0);
|
|
|
+ div.style.left = oEvent.clientX - disX + 'px';
|
|
|
+ div.style.top = oEvent.clientY - disY + 'px';
|
|
|
+ document.onmousemove = function(evt) { //实时改变目标元素obox的位置
|
|
|
+ let oEvent = evt || event;
|
|
|
+ div.style.left = oEvent.clientX - disX + 'px';
|
|
|
+ div.style.top = oEvent.clientY - disY + 'px';
|
|
|
+ }
|
|
|
+ //停止拖动
|
|
|
+ document.onmouseup = function() {
|
|
|
+ document.onmousemove = null;
|
|
|
+ document.onmouseup = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ showDrap(type, data) {
|
|
|
+ this.type = type;
|
|
|
+ this.detail = data;
|
|
|
+ this.drapShow = true;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 背景图片
|
|
|
+ *
|
|
|
+ * */
|
|
|
+ initBackgroundImage() {
|
|
|
+ fabric.Image.fromURL(this.backgroundImageUrl).then(img => {
|
|
|
+ // 获取画布的宽度和高度
|
|
|
+ const canvasWidth = this.canvas.getWidth();
|
|
|
+ const canvasHeight = this.canvas.getHeight();
|
|
|
+ // 计算图片的缩放比例
|
|
|
+ const scaleX = canvasWidth / img.width;
|
|
|
+ const scaleY = canvasHeight / img.height;
|
|
|
+ const scale = scaleX < scaleY ? scaleX : scaleY;
|
|
|
+ // 设置图片的缩放比例
|
|
|
+ img.scale(scale * 0.9);
|
|
|
+ img.set({
|
|
|
+ left: (canvasWidth - (img.width * scale * 0.9)) / 2,
|
|
|
+ top: (canvasHeight - (img.height * scale * 0.9)) / 2,
|
|
|
+ selectable: false
|
|
|
+ })
|
|
|
+ this.canvas.add(img);
|
|
|
+ this.houseList()
|
|
|
+ });
|
|
|
+ },
|
|
|
+ houseList() {
|
|
|
+ let postData = {
|
|
|
+ currPage: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ projectItemTargetId: this.target.id
|
|
|
+ }
|
|
|
+ getHouseListByPage(postData).then(res => {
|
|
|
+ if (res.state) {
|
|
|
+ this.roomList = res.data.dataList.filter(node => node.dataValue).map(node => {
|
|
|
+ let nodes = JSON.parse(node.dataValue);
|
|
|
+ nodes['name'] = node.roomNumber;
|
|
|
+ nodes['id'] = node.id;
|
|
|
+ nodes['data'] = node;
|
|
|
+ return nodes;
|
|
|
+ });
|
|
|
+ this.initFloor();
|
|
|
+ this.setFloor();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ initFloor() {
|
|
|
+ for (let i = 0; i < this.roomList.length; i++) {
|
|
|
+ const box = new fabric.Polygon(this.roomList[i].position, {
|
|
|
+ fill: this.roomList[i].roomColor,
|
|
|
+ stroke: 'rgba(113, 179, 255, 1.0)',
|
|
|
+ strokeWidth: 1,
|
|
|
+ selectable: false,
|
|
|
+ roomId: this.roomList[i].id
|
|
|
+ });
|
|
|
+ this.canvas.add(box);
|
|
|
+ }
|
|
|
+ this.canvas.renderAll();
|
|
|
+ },
|
|
|
+ initRenderRoom(position, color, ) {
|
|
|
+
|
|
|
+ },
|
|
|
+ setFloor() { //设置房间
|
|
|
+ for (var i = 0; i < this.roomList.length; i++) {
|
|
|
+ let boundary = this.roomList[i].position;
|
|
|
+ let arrX = boundary.map(node => node.x);
|
|
|
+ let arrY = boundary.map(node => node.y);
|
|
|
+ let maxX = Math.max(...arrX);
|
|
|
+ let minX = Math.min(...arrX);
|
|
|
+ let maxY = Math.max(...arrY);
|
|
|
+ let minY = Math.min(...arrY);
|
|
|
+ let x = (maxX - minX) / 2 + minX;
|
|
|
+ let y = (maxY - minY) / 2 + minY;
|
|
|
+ if (this.roomList[i].name) {
|
|
|
+ let house = this.roomList[i].data;
|
|
|
+ house['title'] = this.roomList[i].name;
|
|
|
+ this.addText(this.roomList[i].name, y, x, 'roomDetail', house);
|
|
|
+ }
|
|
|
+ if (this.organizationObj[this.roomList[i].id]) {
|
|
|
+ let organization = this.organizationObj[this.roomList[i].id];
|
|
|
+ organization['title'] = this.roomList[i].name;
|
|
|
+
|
|
|
+ this.addOrganizatedName(organization.name, y, x, 'organizationDetail', organization);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addText(name, top, left, detailType, detailData) {
|
|
|
+ // 创建文字对象
|
|
|
+ const text = new fabric.Text(name, {
|
|
|
+ fontSize: 16,
|
|
|
+ fill: 'white',
|
|
|
+ detailType: detailType,
|
|
|
+ detailData: detailData,
|
|
|
+ selectable: false
|
|
|
+ });
|
|
|
+ text.set({
|
|
|
+ top: (top - (text.height / 2) + 10),
|
|
|
+ left: (left - (text.width / 2))
|
|
|
+ })
|
|
|
+ this.canvas.add(text);
|
|
|
+ },
|
|
|
+ addOrganizatedName(name, top, left, detailType, detailData) {
|
|
|
+ // 创建文字对象
|
|
|
+ const text = new fabric.Text(name, {
|
|
|
+ top: 5,
|
|
|
+ left: 10,
|
|
|
+ fontSize: 14,
|
|
|
+ fill: 'white',
|
|
|
+ selectable: false
|
|
|
+ });
|
|
|
+ // 添加矩形
|
|
|
+ const rect = new fabric.Rect({
|
|
|
+ width: text.width + 20, // 矩形的宽度
|
|
|
+ height: text.height + 10, // 矩形的高度
|
|
|
+ fill: 'rgba(15, 70, 255, 0.6)', // 矩形的填充颜色
|
|
|
+ stroke: 'rgba(15, 70, 255, 0.8)', // 矩形的边框颜色
|
|
|
+ strokeWidth: 2, // 矩形的边框宽度
|
|
|
+ });
|
|
|
+ fabric.Image.fromURL('./assets/arrow.png').then(img => {
|
|
|
+ img.scale(0.1);
|
|
|
+ img.set({
|
|
|
+ top: text.height + 15,
|
|
|
+ left: (text.width + 20 - (img.width * 0.1)) / 2
|
|
|
+ })
|
|
|
+ const group = new fabric.Group([rect, text, img], {
|
|
|
+ detailType: detailType,
|
|
|
+ detailData: detailData,
|
|
|
+ selectable: false
|
|
|
+ });
|
|
|
+ group.set({
|
|
|
+ top: (top - group.height),
|
|
|
+ left: (left - (group.width / 2))
|
|
|
+ })
|
|
|
+ // 将文字添加到画布
|
|
|
+ this.canvas.add(group);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+ .operation-plan {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background: $--box-background;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ .fabric-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ #canvas-container {
|
|
|
+ transform-origin: 0% 0%;
|
|
|
+ margin: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ .floor-name {
|
|
|
+ cursor: pointer;
|
|
|
+ width: 60px;
|
|
|
+ text-align: center;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .left-button {
|
|
|
+ position: absolute;
|
|
|
+ z-index: 998;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ left: 0;
|
|
|
+ bottom: 30px;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .menu-item {
|
|
|
+ width: 100px;
|
|
|
+ height: 32px;
|
|
|
+ font-size: 16px;
|
|
|
+ color: #AAB5C7;
|
|
|
+ line-height: 30px;
|
|
|
+ letter-spacing: 2px;
|
|
|
+ text-align: center;
|
|
|
+ background-image: url(../../../assets/image/common/tab.png);
|
|
|
+ background-size: 100% 100%;
|
|
|
+ margin: 0 15px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ .menu-item.active,
|
|
|
+ .menu-item:hover {
|
|
|
+ color: #fff;
|
|
|
+ background-image: url(../../../assets/image/common/tab_active.png);
|
|
|
+ }
|
|
|
+
|
|
|
+ .distribution-drap {
|
|
|
+ position: absolute;
|
|
|
+ top: 30px;
|
|
|
+ right: 30px;
|
|
|
+ background: $--color-background;
|
|
|
+ width: 300px;
|
|
|
+ border-radius: 8px;
|
|
|
+ z-index: 998;
|
|
|
+ right: 30px;
|
|
|
+ top: 30px;
|
|
|
+
|
|
|
+ .distribution-drap-content {
|
|
|
+ padding-bottom: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .distribution-drap-title {
|
|
|
+ height: 40px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .distribution-drap-title-content {
|
|
|
+ flex: 1;
|
|
|
+ width: 0;
|
|
|
+ font-weight: 500;
|
|
|
+ cursor: move;
|
|
|
+ padding-left: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-icon-circle-close {
|
|
|
+ font-size: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ padding-right: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-list {
|
|
|
+ padding: 0 10px 0px 10px;
|
|
|
+ font-size: 13px;
|
|
|
+
|
|
|
+ .user-item {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 2px;
|
|
|
+
|
|
|
+ .user-key {
|
|
|
+ width: 84px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-value {
|
|
|
+ flex: 1;
|
|
|
+ width: 0;
|
|
|
+ margin-left: 2px;
|
|
|
+ overflow: hidden;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-item>div {
|
|
|
+ background: #232A37;
|
|
|
+ line-height: 34px;
|
|
|
+ padding: 0 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-item:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-item-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 150px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ user-select: none;
|
|
|
+ -webkit-user-drag: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .video-mask {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ z-index: 2;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ opacity: 0;
|
|
|
+ transition: 300ms;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ i {
|
|
|
+ font-size: 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-item-image:hover {
|
|
|
+ .video-mask {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+</style>
|