This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nezha-nezha-fronted/nezha-fronted/src/components/common/project/topologyL5.vue

2641 lines
82 KiB
Vue
Raw Normal View History

<style>
@import "./L5/css/iconfont.css";
@import "./L5/css/props.css";
</style>
<template>
2021-02-05 13:35:47 +08:00
<div class="project-box" v-loading="topologyLoading" :style="{'border':fromOverView?'none':'1px solid #eeeeee'}">
2021-02-05 10:44:07 +08:00
<!--project主要信息-->
2021-02-09 16:27:22 +08:00
<div class="project-title" v-if="showTopTools&&!fromOverView" :style="{'background':editTopologyFlag?'#F6F6F6':'#ffffff','border-bottom':editTopologyFlag? '1px solid #F6F6F6':'' }">
2021-02-05 10:44:07 +08:00
<div v-show="editTopologyFlag" class="edit-topologyLine" style="padding-left: 20px">
<!--工具栏-->
<span class="project-topology-tool">
2021-02-02 10:30:45 +08:00
<el-dropdown trigger="click" size="small" placement="bottom-start">
<span class="el-dropdown-title"><i class="iconfont icon-cube"></i> <i
class="nz-icon nz-icon-arrow-down"></i></span>
2021-02-02 10:30:45 +08:00
<el-dropdown-menu slot="dropdown" @click="dropdownClick">
<div style="height: 450px">
<el-card shadow="hover" style="height:420px;width:284px;overflow-y: auto"
class="project-topology-add-node">
2021-02-02 10:30:45 +08:00
<!--<div class="drag-header"></div>-->
<el-collapse v-model="activeNames" v-for="(item, index) in tools" :key="index">
<el-collapse-item :title="item.group" :name="item.group">
<div v-for="(btn, i) in item.children" :key="'info2'+'-'+index+'-'+i" class="buttons">
<a
:key="i"
:title="btn.data.text"
:draggable="btn.data"
@dragstart.stop="onDrag($event, btn)"
@mousedown="dragFlag=false"
@mouseup="dragFlagChange(btn)"
class="btn"
>
<img :src="btn.data.image" v-if="btn.data.image">
<i v-else :class="`iconfont ${btn.icon}`"></i>
</a>
<span v-if="item.group==='自定义图片'" class="delIcon" @click="delImg(btn)">x</span>
</div>
</el-collapse-item>
</el-collapse>
</el-card>
<div class="upload-pic-box" @click="uploadPicChange">
2021-02-02 10:30:45 +08:00
<i class="el-icon-plus"></i>
<span>
Upload custom picture
</span>
</div>
2021-02-02 10:30:45 +08:00
</div>
</el-dropdown-menu>
</el-dropdown>
<div class="flex middle special-select mb10"
style="width: 75px;height: 28px;display: inline-block;margin: 0 40px 0 20px;background: #fff">
2021-02-02 10:30:45 +08:00
<div class="full pr10">
<el-select v-model="lineName" size="small"
:popper-append-to-body="false"
@change="changeTopologyOpt(lineName,'lineName')">
2021-02-02 10:30:45 +08:00
<div slot="prefix">
<div class="icon-item" style="width: 100%;padding: 0">
2021-02-02 10:30:45 +08:00
<svg>
<g fill="none" stroke="black" stroke-width="1">
<path
:d="penLineType.find((item,i)=>item.id==lineName).d"
2021-02-02 10:30:45 +08:00
>
</path>
</g>
</svg>
</div>
</div>
<el-option :disabled="true" :value="false">{{$t('project.topology.defaultLineType')}}</el-option>
<el-option v-for="(item,index) in penLineType" :value="item.id" :key="index">
<div class="icon-item" style="position: relative;width: 100%;padding: 0">
2021-02-02 10:30:45 +08:00
<svg>
<g fill="none" :stroke="(lineName==item.name)?'#ee9d3f':'black'" stroke-width="1">
<path :d="item.d" :stroke-dasharray="item['stroke-dasharray']"></path>
</g>
</svg>
<span style="position: absolute;left:60px;top: 0;">{{item.name}}</span>
2021-02-02 10:30:45 +08:00
</div>
</el-option>
</el-select>
</div>
</div>
2021-02-02 10:30:45 +08:00
<topology-top-tool
v-if="editTopologyFlag"
:selection.sync="props"
@del="delPen"
:index="topologyIndex"
ref="topTool"
@toolShowChange="toolShowChange"
:cachesIndex="cachesIndex"
:redoIndexChange="redoIndexChange"
2021-02-02 10:30:45 +08:00
:toolShow="toolShow">
</topology-top-tool>
2021-02-02 10:30:45 +08:00
</span>
2021-02-02 10:30:45 +08:00
<span class="float-right">
<button @click="previewTopology" class="nz-btn nz-btn-size-normal-new nz-btn-style-light-new"
2021-02-05 10:44:07 +08:00
style="margin-right: 20px"
>
2021-02-02 10:30:45 +08:00
{{$t('project.topology.preview')}}
</button>
<button @click="saveTopology" class="nz-btn nz-btn-size-normal-new nz-btn-style-normal-new"
v-has="'project_topo_save'" :disabled="prevent_opt.save"
2021-02-05 10:44:07 +08:00
:class="{'nz-btn-disabled':prevent_opt.save}"
style="margin-right: 20px">
{{$t('project.topology.save')}}
</button>
2021-02-05 10:44:07 +08:00
<button @click="cancelTopology" class="nz-btn nz-btn-size-normal-new nz-btn-style-normal-new" style="margin-right: 20px">
{{$t('project.topology.exit')}}
</button>
</span>
</div>
2021-02-09 16:27:22 +08:00
<div style="width: 100%;display: flex;justify-content: space-between" v-if="!editTopologyFlag&&!fromOverView">
<div class="facade-top">
<div class="facade-top-left" v-loading="projectInfo.loading" v-if="projectInfoShow">
<div class="facade-top-title">
Project information
</div>
<div>
<!--<span><span class="label">Id :</span>{{projectInfo.id}}</span>-->
<span><span class="label">Name :</span>{{projectInfo.name}}</span>
</div>
<div>
<span><span class="label">Description :</span>{{projectInfo.remark?projectInfo.remark:'--'}}</span>
</div>
<!--<div>-->
<!--<span>-->
<!--<span class="label">Alert state :</span>-->
<!--<div class="active-icon" style="background: #B7464A 100%;"></div>{{projectInfo.alertStat[0]}}-->
<!--<div class="active-icon" style="background: #E64E4E 100%;"></div>{{projectInfo.alertStat[1]}}-->
<!--<div class="active-icon" style="background: #F7B500 100%;"></div>{{projectInfo.alertStat[2]}}-->
<!--</span>-->
<!--</div>-->
<div>
<span><span class="label">Module Num :</span>{{projectInfo.moduleMum}}</span>
</div>
</div>
<div class="facade-top-right" v-loading="projectInfo.loading" v-if="projectAlertShow" style="padding: 20px 20px 0 20px;height: calc(100% - 20px);">
<div class="facade-top-title">
<span class="label" style="padding-left: 0;">Alert :</span>
{{projectInfo.total}}
</div>
<div class="facade-top-right-content">
<div>
<div class="content-P1-title">
{{returnSeverityLabel('P1')}}
</div>
<div>
{{projectInfo.alertStat[0] || 0}}
</div>
</div>
<div>
<div class="content-P2-title">
{{returnSeverityLabel('P2')}}
</div>
<div>
{{projectInfo.alertStat[1] || 0}}
</div>
</div>
<div style="margin-bottom: 20px;">
<div class="content-P3-title">
{{returnSeverityLabel('P3')}}
</div>
<div>
{{projectInfo.alertStat[2] || 0}}
</div>
</div>
</div>
</div>
</div>
2021-02-09 16:27:22 +08:00
<div :style="{flex:1,'padding-right': '15px','text-align':topologyInfo.align,'font-size':topologyInfo.fontSize,'color':topologyInfo.fontColor,opacity:topologyInfo.opacity}">{{topologyInfo.name}}</div>
<span class="edit-topologyLine" style="padding-top: 5px" v-show="!editTopologyFlag&&!fromPrev&&!fromOverView">
<button @click="editTopology" class="nz-btn nz-btn-size-normal nz-btn-style-light float-right"
style="border-right: 1px solid rgba(162,162,162,0.50);margin-right: 12px;margin-top: -2px" type="button" v-has="'project_topo_save'"
>
<i class="nz-icon nz-icon-edit" :title="$t('project.topology.edit')"></i>
</button>
<pick-time
v-show="!editTopologyFlag"
class="float-right pickTime"
:refresh-data-func="dateChange"
v-model="searchTime"
:use-chart-unit="false"
ref="pickTime">
</pick-time>
</span>
</div>
</div>
<div :class="['page',fromOverView?'overview-page':'']">
<!--画布部分-->
2021-02-04 18:01:57 +08:00
<div :id="'topology-canvas' + topologyIndexF" class="full" :ref="'topology-canvas'+ topologyIndexF"></div>
<!--设置属性-->
2021-02-02 10:30:45 +08:00
<div class="props" v-if="editTopologyFlag&&toolShow.attr">
<CanvasProps :selection.sync="props"
@change="onUpdateProps"
@animate="animateCanvas"
2021-02-09 16:27:22 +08:00
@changeProjectTitle="changeProjectTitle"
@notModuleIDArrChange="notModuleIDArrChange"
2021-02-02 10:30:45 +08:00
:index="topologyIndex"
@del="delPen"
:modules="modules"
ref="CanvasProps">
</CanvasProps>
</div>
<!--所有节点上的小图标-->
2021-03-19 18:52:19 +08:00
<div v-for="(item,index) in nodesArr" :key="index"
:style="{position: 'absolute',top:item.rect.y - (48*(fromOverView?penToolTipScale:1))+'px',left:item.rect.center.x - (24*(fromOverView?penToolTipScale:1)) +'px',transform:'scale('+(fromOverView?penToolTipScale:1)+')'}"
2021-02-05 13:35:47 +08:00
v-if="!editTopologyFlag&&item.data.iconToolState&&!fromPrev"
2021-02-02 10:30:45 +08:00
class="network-pop"
>
2021-02-02 10:30:45 +08:00
<i
:class="{'nz-icon':true, 'nz-icon-shuidi':true,'model-error':item.data.state&&item.data.state.error&&!item.data.show,'model-error-active':item.data.state&&item.data.state.error&&item.data.show}"
2021-02-02 10:30:45 +08:00
:ref="'modelTopId'+index"
@click="showNodeTools(index,item)"
>
<i class="nz-icon nz-icon-model"></i>
</i>
<!--'selpop':selpopIs(item),'no-selPop':!selpopIs(item),'error-model-stat':modelPopError(item) @click="popClick(item.id)" -->
2021-03-19 18:52:19 +08:00
<div v-for="(item1, index) in popData" :key="index">
<transition name="scaleTool">
<i v-if="item.data.show"
2021-02-05 15:26:27 +08:00
:class="{'nz-icon':true,'nz-icon-hexagonBorder':true,'error-model-stat':item.data.state[item1.id],'selpop':selpopIs(item,item1),'no-selPop':!selpopIs(item,item1),}"
:style="{top:item1.top,left:item1.left}"
:title="item1.title"
@click="nodeTools(item,item1)"
>
<i class="nz-icon nz-icon-liubianxing"></i>
<i :class="[item1.className,{'nz-icon':item1.className},'noMove']"></i>
</i>
</transition>
</div>
</div>
<!--提示未添加module id的块-->
2021-03-19 18:52:19 +08:00
<div v-for="(item, index) in notModuleIDArr"
:key="index"
:style="{
2021-03-19 18:52:19 +08:00
position: 'absolute',
top:item.rect.y- 10 +'px',
left:item.rect.x - 10+'px',
transform:'scale('+(fromOverView?penToolTipScale:1)+')',
}"
v-if="editTopologyFlag&&!fromPrev"
>
<div class="module-rect-top" :style="{top:0,left:0,width:item.rect.width+15+'px',height:0}"></div>
<div class="module-rect-right" :style="{top:0,left:item.rect.width+15+'px',width:0,height:item.rect.height+15+'px'}"></div>
<div class="module-rect-bottom" :style="{top:item.rect.height+15 +'px',left:0,width:item.rect.width+15+'px',height:0}"></div>
<div class="module-rect-left" :style="{top:0,left:0,width:0,height:item.rect.height+15+'px'}"></div>
</div>
<!--节点连线相关的 tooltip-->
<div :style="{position:'absolute',top:tooltipPosition.top+'px',left:tooltipPosition.left+'px','z-index':10,height:tooltipPosition.height+'px'}"
v-if="tooltipPosition.show&&!editTopologyFlag"
@mouseover="tooltipOver"
@mouseout="tooltipOut"
ref="topoTooltip"
>
2021-02-04 17:06:04 +08:00
<topoTooltip :chartDataParent="chartData" :filterTime="filterTime"/>
</div>
</div>
2021-02-02 10:30:45 +08:00
<!--<div class="left-bottom" v-if="editTopologyFlag">-->
<!--<div class="title">小提示</div>-->
<!--<ul class="group">-->
<!--<li>编辑时</li>-->
<!--<li>1.Ctrl + 鼠标移动移动整个画布</li>-->
<!--<li>2.Ctrl + 鼠标滚轮缩放</li>-->
<!--<li>3.选中元素 按下Delete键或者Backspace可以删除元素</li>-->
<!--</ul>-->
2021-02-02 10:30:45 +08:00
<!--</div>-->
<!--悬浮network部分-->
<div class="network-info">
2021-02-02 10:30:45 +08:00
<div v-if="popDataShow.main">
<popDataMain :moduleId="moduleId" :projectId="projectInfo.id"></popDataMain>
</div>
<div v-if="popDataShow.info">
<popDataInfo :moduleId="moduleId" :projectId="projectInfo.id"></popDataInfo>
</div>
</div>
<!--endpoint-->
<transition name="right-box">
<endpointTable v-if="popDataShow.endpoint" :moduleId="moduleId" :projectId="projectInfo.id"
@close="popDataShowUpdate('',true)">endpoint
</endpointTable>
</transition>
<!--asset-->
<transition name="right-box">
<assetTable v-if="popDataShow.asset" :moduleId="moduleId" :projectId="projectInfo.id"
@close="popDataShowUpdate('',true)">alert
</assetTable>
</transition>
<!--alert-->
<transition name="right-box">
<alertTable v-if="popDataShow.alert" :moduleId="moduleId" :projectId="projectInfo.id"
@close="popDataShowUpdate('',true)">alert
</alertTable>
</transition>
2021-02-05 10:44:07 +08:00
<!--preview-->
<el-dialog
:visible.sync="previewShow"
:width="'80%'"
:before-close="previewBeforeClose"
2021-02-05 10:44:07 +08:00
>
<div style="width: calc(80vw - 40px);height: 80vh">
<topologyPrev
2021-02-05 10:44:07 +08:00
v-if="previewShow"
:obj="obj"
:topoPrevDataS="topoPrevData"
:fromOverView="false"
2021-02-05 10:44:07 +08:00
:fromPrev="true"
:topologyIndexF="1">
</topologyPrev>
2021-02-05 10:44:07 +08:00
</div>
</el-dialog>
<!--Custom picture-->
<el-dialog
title="Custom picture"
:visible.sync="uploadPicShow"
width="auto"
@close="uploadPicShow = false"
destroy-on-close>
<el-row class="upload-pic-row">
<el-col :span="4" class="upload-pic-label">Name</el-col>
<el-col :span="20">
<el-input v-model="uploadPic.name" size="small" :placeholder="$t('project.topology.placeholderImg')"></el-input>
</el-col>
</el-row>
<el-row class="upload-pic-row">
<el-col :span="4" class="upload-pic-label">Folder</el-col>
<el-col :span="20">
<el-autocomplete
class="inline-input"
v-model="uploadPic.unit"
:fetch-suggestions="querySearch"
size="small"
></el-autocomplete>
</el-col>
</el-row>
<el-row class="upload-pic-row">
<el-col :span="4" class="upload-pic-label"> </el-col>
<el-col :span="20">
<div class="upload-body">
<el-upload
drag
class="upload-demo"
action=" "
:show-file-list="true"
:on-change="beforeAvatarUpload"
:auto-upload="false"
accept=".jpg,.png"
:limit="1"
:id="'upload-pic-show'">
<!--<div slot="tip" class="el-upload__tip" >{{$t('overall.importTipImg')}}</div>-->
<i class="nz-icon nz-icon-upload"></i>
<div class="el-upload__text">{{$t('overall.dragFileTip')}}{{$t('overall.or')}}&nbsp;<em>{{$t('overall.clickUpload')}}</em></div>
</el-upload>
</div>
</el-col>
</el-row>
<div class="upload-pic-row" style="text-align: center">
<span>
<button @click="uploadPicShow=false" class="nz-btn nz-btn-size-normal-new nz-btn-style-light-new" style="margin-right: 20px">
{{$t('project.topology.exit')}}
</button>
<button @click="imgUpload" class="nz-btn nz-btn-size-normal-new nz-btn-style-normal-new"
v-has="'project_topo_save'" :disabled="prevent_opt.save"
:class="{'nz-btn-disabled':prevent_opt.save}"
style="margin-right: 20px">
{{$t('project.topology.save')}}
</button>
</span>
</div>
</el-dialog>
2021-03-04 16:11:11 +08:00
<div class="right-bottom-zoom" v-if="!fromOverView&&!editTopologyFlag&&!fromPrev">
<div class="zoom-option" style="border-bottom: 1px solid #c5c8cb;" @click="zoomMap(0.25)"><span><i class="nz-icon nz-icon-enlarge"></i></span></div>
<div class="zoom-option" @click="zoomMap(-0.25)"><span><i class="nz-icon nz-icon-narrow"></i></span></div>
</div>
</div>
</template>
<script>
2021-03-19 18:52:19 +08:00
import { Topology, registerNode } from '@topology/core'
import {
Tools,
canvasRegister,
imageTemp,
myShape,
myAnchors,
myIconRect,
myTextRect,
onChangeAnimate,
onChangeAnimateLine,
myCubec,
myCubeAnchors
} from './L5/services/canvas.js'
import { getTopology, setTopology } from '../js/common'
import CanvasProps from './L5/CanvasProps'
import topologyTopTool from './L5//topologyTopTool'
import popDataMain from './popData/Main'
import popDataInfo from './popData/Info'
import alertTable from './popData/alertTable'
import assetTable from './popData/assetTable'
import endpointTable from './popData/endpointTable'
import topoTooltip from './L5/topoTooltip'
import { getMetricTypeValue } from '../js/tools'
import bus from '../../../libs/bus'
import topologyPrev from './topologyPrev'
// 注册到画布
registerNode('rectangleImg', myShape, myAnchors, myIconRect, myTextRect)
registerNode('myCube', myCubec, myCubeAnchors, null, null)
const canvasOptions = {
rotateCursor: '/img/rotate.cur',
translateKey: 'None',
disableEmptyLine: false,
2021-03-19 18:52:19 +08:00
autoExpandDistance: 0,
minScale: 0.01
}
export default {
name: 'topologyL5',
data () {
return {
objChange: false, // project 变化 用于判断 init是否执行完成 执行完成 才可以执行下次变化
chartDataInfo: {},
topoPrevData: {}, // 预览数据
imgInit: false, // 判断图片是否加载完成
toolGroup: '基本形状',
editFlag: true,
tools: Tools,
props: {},
topologyLoading: false,
contextmenu: {
left: null,
top: null,
bottom: null
},
2021-03-19 18:52:19 +08:00
filterTime: [
bus.timeFormate(bus.getOffsetTimezoneData(-1), 'yyyy-MM-dd hh:mm:ss'),
bus.timeFormate(bus.getOffsetTimezoneData(), 'yyyy-MM-dd hh:mm:ss')
],
topologyInfo: {
fontSize: 14,
align: 'left',
fontColor: '#000000',
opacity: 1,
name: ''
},
2021-03-19 18:52:19 +08:00
saveData: {},
oldTopologyData: '',
redoIndex: 0,
dataLength: 0,
editTopologyFlag: false,
searchTime: bus.getTimezontDateRange(),
activeNames: [],
topologyIndex: 0,
iconArray: [],
imgageLoading: false,
toolShow: {
node: true,
attr: true,
nodeCord: [0, 0],
attrCord: [0, 0],
height: 500
2021-02-05 10:44:07 +08:00
},
2021-03-19 18:52:19 +08:00
dragFlag: true,
modules: [],
allModules: [],
projectInfo: {
title: '',
id: '',
remark: '',
alertStat: [1, 2, 3],
moduleMum: 6,
loading: true
2021-02-05 10:44:07 +08:00
},
2021-03-19 18:52:19 +08:00
timer: null, // 处理project短时间呢频繁变更的定时器
timer2: null, // 处理平移画布显示iconState的定时器
timer3: null, // 处理tooltip的显示定时器
nodesArr: [],
notModuleIDArr: [],
popData: [
{
top: '-40px',
left: '-21px',
className: 'nz-icon-endpoint',
id: 'endpoint',
title: this.$t('project.topology.endpoint')
},
{ top: '-40px', left: '19px', className: 'nz-icon-asset', id: 'asset', title: this.$t('project.topology.asset') },
{ top: '-4px', left: '40px', className: '', id: 'other', title: '' },
{ top: '30px', left: '19px', className: '', id: 'other', title: '' },
{ top: '30px', left: '-21px', className: 'nz-icon-info-normal', id: 'info', title: this.$t('project.topology.info') },
{ top: '-4px', left: '-40px', className: 'nz-icon-gaojing', id: 'alert', title: this.$t('project.topology.alert') }
],
popDataShow: {
endpoint: false,
asset: false,
total: false,
other: false,
info: false,
alert: false,
main: false
2021-02-05 10:44:07 +08:00
},
2021-03-19 18:52:19 +08:00
moduleId: '',
tooltipPosition: {
top: 0,
left: 0,
show: false,
height: 400
},
2021-03-19 18:52:19 +08:00
chartData: {},
chartGetData: [],
penLineType: [
{ d: 'M5 19 a50,100 0 0,1 40,0', 'stroke-dasharray': '', name: this.$t('project.topology.curve'), id: 'curve' },
{ d: 'M5 8 l20 0 l0 12 l20 0', 'stroke-dasharray': '', name: this.$t('project.topology.polyline'), id: 'polyline' },
{ d: 'M5 14 l40 0', 'stroke-dasharray': '', name: this.$t('project.topology.line'), id: 'line' }
// {d:'M5 20 C0,8 50,0 85,0',"stroke-dasharray":"",name:'mind'},
],
lineName: 'curve',
cachesIndex: 0,
projectInfoShow: false,
projectAlertShow: false,
previewShow: false,
penId: undefined,
penToolTipScale: 1,
oldScale: 1,
uploadPicShow: false,
uploadPic: {
name: '',
unit: ''
},
unitArr: []
}
},
components: {
CanvasProps,
topologyTopTool,
popDataMain,
popDataInfo,
alertTable,
assetTable,
endpointTable,
topoTooltip,
topologyPrev
},
props: {
topologyIndexF: {
type: Number,
default: 0
},
obj: {},
showTopTools: {
type: Boolean,
default: true
},
fromOverView: {
type: Boolean,
default: false
},
fromPrev: {
type: Boolean,
default: false
},
topoPrevDataS: {
2021-03-19 18:52:19 +08:00
}
},
watch: {
topologyIndexF: {
immediate: true,
handler (n) {
this.topologyIndex = n
}
},
2021-03-19 18:52:19 +08:00
obj: {
deep: true,
immediate: true,
handler (n) {
if (n.id) {
if (getTopology(this.topologyIndex)) {
getTopology(this.topologyIndex).destroy()
setTopology(this.topologyIndex, null)
}
2021-03-19 18:52:19 +08:00
if (!this.objChange) {
this.editTopologyFlag = false
this.topologyLoading = true
this.projectInfoShow = false
this.projectAlertShow = false
if (n.id) {
this.getProjectData(n)
}
this.topologyLoading = true
this.init()
this.timer = null
this.objChange = true
} else {
if (!this.timer) {
this.timer = setInterval(() => {
if (this.objChange) { return }
this.editTopologyFlag = false
this.topologyLoading = true
this.projectInfoShow = false
this.projectAlertShow = false
if (n.id) {
this.getProjectData(n)
}
clearTimeout(this.timer)
this.timer = null
this.objChange = true
this.topologyLoading = true
this.init()
}, 100)
} else {
clearTimeout(this.timer)
this.timer = setInterval(() => {
if (this.objChange) { return }
this.editTopologyFlag = false
this.topologyLoading = true
this.projectInfoShow = false
this.projectAlertShow = false
if (n.id) {
this.getProjectData(n)
}
clearTimeout(this.timer)
this.timer = null
this.objChange = true
this.topologyLoading = true
this.init()
}, 100)
}
}
}
}
2021-03-19 18:52:19 +08:00
}
},
computed: {},
created () {
canvasRegister()
if (process.client) {
document.onclick = event => {
this.contextmenu = {
left: null,
top: null,
bottom: null
}
}
2021-03-19 18:52:19 +08:00
}
},
mounted () {
if (!this.fromOverView) { // 从overview来的 加载相应图片 优化首页加载速度
this.addNodeInit()
}
document.getElementById('topology-canvas' + this.topologyIndexF).addEventListener('mousemove', this.canvasMove)
window.addEventListener('resize', this.winResize)
},
methods: {
init () {
canvasOptions.on = this.onMessage
this.reload()
},
2021-03-19 18:52:19 +08:00
reload () {
this.topologyLoading = true
this.getTopologyData().then((data) => {
this.openTopologyData(data).then(() => {
// 获取对应的值 给节点 连线添加对应动画
this.lineName = data.lineName ? data.lineName : this.lineName
this.chartGetData = []
const axiosArr = []
const promiseArr = []
const self = this
const pensPromise = (pen, arr, index) => {
return new Promise(function (resolve, reject) {
Promise.all(arr).then((res) => {
self.chartGetData[index].res = self.computeData(res, pen.data.aggregation, pen)
self.setAnimation(pen, self.chartGetData[index].res)
resolve()
})
})
}
const endTime = this.filterTime[1]
const startTime = this.filterTime[0]
const step = bus.getStep(startTime, endTime)
data.pens && data.pens.forEach((item, index) => {
this.chartGetData.push({ id: item.id, res: [] })
const arr = item.data.expressArr.map((ele) => {
let query = encodeURIComponent(ele)
if (!query) {
return new Promise(resolve => {
resolve({ data: '', status: 'no query' })
})
}
query += '&nullType=' + 'connected'
return this.$get('/prom/api/v1/query_range?query=' + query + '&start=' + this.$stringTimeParseToUnix(startTime) + '&end=' + this.$stringTimeParseToUnix(endTime) + '&step=' + step)
})
axiosArr.push({ item, arr })
promiseArr.push(pensPromise(item, arr, index))
})
Promise.all(promiseArr).then((res) => {
getTopology(this.topologyIndex).open(data)
getTopology(this.topologyIndex).lock(1)
this.objChange = false
let flag = false
const position = {
x: this.$refs['topology-canvas' + this.topologyIndexF].offsetWidth,
y: this.$refs['topology-canvas' + this.topologyIndexF].offsetHeight
}
this.oldScale = getTopology(this.topologyIndex).data.scale
getTopology(this.topologyIndex).data.pens.forEach(item => {
if (flag) {
return
}
if (item.rect.ex > position.x || item.rect.ey > position.y) {
getTopology(this.topologyIndex).fitView(20)
flag = true
}
})
getTopology(this.topologyIndex).centerView(20)
this.penToolTipScale = getTopology(this.topologyIndex).data.scale
setTimeout(() => {
getTopology(this.topologyIndex).data.pens.forEach(item => {
if (item.animatePlay) {
item.stopAnimate()
2021-03-19 18:52:19 +08:00
setTimeout(() => {
item.startAnimate()
})
}
2021-03-19 18:52:19 +08:00
}, 100)
})
// if(this.fromPrev){
// getTopology(this.topologyIndex).scaleTo(data.scale/2)
// }
// getTopology(this.topologyIndex).fitView();
this.oldTopologyData = JSON.stringify(getTopology(this.topologyIndex).data)
this.getNodesArr()
2021-02-02 19:24:21 +08:00
})
2021-03-19 18:52:19 +08:00
})
})
},
2021-03-19 18:52:19 +08:00
dateChange () {
const nowTimeType = this.$refs.pickTime.$refs.timePicker.nowTimeType
this.setSearchTime(nowTimeType.type, nowTimeType.value)
this.filterTime[0] = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss')
this.filterTime[1] = bus.timeFormate(this.searchTime[1], 'yyyy-MM-dd hh:mm:ss')
this.reload()
},
setSearchTime (type, val) { // 设置searchTime
if (type === 'minute') {
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setMinutes(new Date(bus.computeTimezone(new Date().getTime())).getMinutes() - val), 'yyyy-MM-dd hh:mm:ss')
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
this.$set(this.searchTime, 0, startTime)
this.$set(this.searchTime, 1, endTime)
this.$set(this.searchTime, 2, val + 'm')
} else if (type === 'hour') {
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setHours(new Date(bus.computeTimezone(new Date().getTime())).getHours() - val), 'yyyy-MM-dd hh:mm:ss')
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
this.$set(this.searchTime, 0, startTime)
this.$set(this.searchTime, 1, endTime)
this.$set(this.searchTime, 2, val + 'h')
} else if (type === 'date') {
const startTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())).setDate(new Date(bus.computeTimezone(new Date().getTime())).getDate() - val), 'yyyy-MM-dd hh:mm:ss')
const endTime = bus.timeFormate(new Date(bus.computeTimezone(new Date().getTime())), 'yyyy-MM-dd hh:mm:ss')
this.$set(this.searchTime, 0, startTime)
this.$set(this.searchTime, 1, endTime)
this.$set(this.searchTime, 2, val + 'd')
}
this.$refs.pickTime.$refs.timePicker.searchTime = this.searchTime
},
// 打开topology数据
openTopologyData (data) {
return new Promise(resolve => {
if (!getTopology(this.topologyIndex)) {
const canvas = new Topology('topology-canvas' + this.topologyIndexF, canvasOptions)
canvas.open(data)
setTopology(this.topologyIndex, canvas)
} else {
getTopology(this.topologyIndex).open(data)
}
2021-03-19 18:52:19 +08:00
this.topologyLoading = false
if (!getTopology(this.topologyIndex).data.name) {
getTopology(this.topologyIndex).data.name = this.obj.name
}
getTopology(this.topologyIndex).data.projectId = this.obj.id
getTopology(this.topologyIndex).lock(1)
2021-02-04 17:06:04 +08:00
2021-03-19 18:52:19 +08:00
this.getModule()// 获取module
resolve()
})
},
// 获取topology数据
getTopologyData () {
return new Promise(resolve => {
if (this.fromPrev) {
resolve(this.topoPrevDataS)
}
this.$get('/project/topo', { projectId: this.obj.id }).then(res => {
let data = res.data.topo
if (!res.data.topo || !data.pens) {
data = {
bkColor: '#FFFFFF',
gridSize: 10,
gridColor: '#ededed',
lineWidth: 1,
ruleColor: '#4e4e4e'
}
this.projectInfoShow = true
this.projectAlertShow = true
this.saveData = { ...data }
this.topologyInfo.name = this.obj.name
resolve(data)
} else {
this.topologyInfo = {
fontSize: data.data.fontSize,
align: data.data.align,
fontColor: data.data.fontColor,
opacity: data.data.opacity,
name: data.name
}
if (this.fromOverView) { // 优化从首页来的加载速度
const arr = data.pens.filter(item => !item.type)
this.addNodeInit(arr)
}
const timer = setInterval(() => {
if (!this.imgInit) {
return
}
2021-03-19 18:52:19 +08:00
clearInterval(timer)
const promiseArr = []
const self = this
// for(let i=0;i<data.pens.length;i++){
// let line=data.pens[i]
// if(line.type===1){
// if(!data.pens.find(item=>item.id===line.from.id) || !data.pens.find(item=>item.id===line.to.id)){
// data.pens.splice(i, 1);
// i--;
// }
// }
// }
data.pens.forEach(item => {
if (item.type === 0 && item.data.imageId) {
item.image = this.iconArray.find(item1 => item1.id == item.data.imageId).image
2021-02-05 10:44:07 +08:00
}
2021-03-19 18:52:19 +08:00
if (item.type === 0) {
promiseArr.push(this.$get('/module/stat', { id: item.data.moduleId }))
} else {
promiseArr.push({ type: 1 })
}
2021-03-19 18:52:19 +08:00
})
if (!data.data) {
this.projectInfoShow = true
this.projectAlertShow = true
} else if (!JSON.stringify(data.data.projectInfo)) {
this.projectInfoShow = true
this.projectAlertShow = true
} else {
this.projectInfoShow = data.data.projectInfo
this.projectAlertShow = data.data.alertInfo
}
if (!data.bkImage) {
data.bkImage = undefined
}
Promise.all(promiseArr).then(res => {
res.forEach((response, index) => {
const item = data.pens[index]
if (item.type === 0) {
item.data.state = response.data
item.data.state.asset = false
item.data.state.endpoint = false
item.data.state.alert = false
if (item.data.state.assetStat.down > 0) {
item.data.state.asset = true
}
if (item.data.state.endpointStat.down > 0) {
item.data.state.endpoint = true
}
if (item.data.state.alertStat.P1 > 0 || item.data.state.alertStat.P3 > 0 || item.data.state.alertStat.P2 > 0) {
item.data.state.alert = true
}
2021-03-19 18:52:19 +08:00
item.data.state.error = item.data.state.asset || item.data.state.endpoint || item.data.state.alert
}
})
2021-03-19 18:52:19 +08:00
self.saveData = { ...data }
resolve(data)
})
2021-03-19 18:52:19 +08:00
}, 100)
}
2021-03-19 18:52:19 +08:00
})
})
},
// 赋值动画
setAnimation (pen, res) { // 根据所有res的状态 赋值动画
let maxLevel = 0
if (res.length > 0) {
res.forEach((response, innerPos) => {
if (response.status !== 'success') {
return
}
2021-03-19 18:52:19 +08:00
if (response.data.result) {
response.data.result.forEach((queryItem, resIndex) => {
pen.data.valueMapping.forEach((item, index) => {
if (item.value === 'base') { return }
if (queryItem.showValue > item.value) {
queryItem.level = item.level
if (maxLevel < item.level) {
maxLevel = item.level
}
}
})
})
}
2021-03-19 18:52:19 +08:00
})
}
if (maxLevel !== 0) {
if (pen.type === 0) { // 判断valueMapping 给相应的状态
const selLevel = pen.data.valueMapping.find(item => item.level === maxLevel)
if (selLevel) {
pen.fontColor = selLevel.color.text
pen.fillStyle = selLevel.color.fill
pen.strokeStyle = selLevel.color.line
pen.bkType = 0
}
onChangeAnimate(pen, selLevel.animateType, selLevel.color.fill, selLevel.color.line)
} else if (pen.type === 1) { // 判断valueMapping 给相应的状态
const selLevel = pen.data.valueMapping.find(item => item.level === maxLevel)
if (selLevel) {
pen.animateColor = selLevel.color.fill
pen.strokeStyle = selLevel.color.line
pen.animateType = selLevel.animateType
pen.fontColor = selLevel.color.text
}
onChangeAnimateLine(pen, pen.animateType)
}
2021-03-19 18:52:19 +08:00
} else {
if (pen.type === 0 && pen.animatePlay) { // 判断valueMapping 给相应的状态
onChangeAnimate(pen, pen.animateType)
} else if (pen.type === 1 && pen.animatePlay) { // 判断valueMapping 给相应的状态
onChangeAnimateLine(pen, pen.animateType)
}
}
},
computeData (res, type, pen) { // 处理别名 以及 根据type显示对应的值
if (res.length > 0) {
res.forEach((response, innerPos) => {
if (response.status !== 'success') {
return
}
2021-03-19 18:52:19 +08:00
if (response.data.result) {
response.data.result.forEach((queryItem, resIndex) => {
// 图表中每条线的名字,后半部分
let host = ''// up,
if (queryItem.metric.__name__) {
host = `${queryItem.metric.__name__}{`// up,
}
const tagsArr = Object.keys(queryItem.metric)// ["__name__","asset","idc","instance","job","module","project"]
// 设置时间-数据格式对
let dpsArr = []
dpsArr = Object.entries(queryItem.values)// [ ["0",[1577959830.781,"0"]], ["1",[1577959845.781,"0"]] ]
dpsArr = dpsArr.map(item => {
return [item[0], [item[1][0], Number(item[1][1])]]
})
// 判断是否有数据, && tagsArr.length > 0
if (dpsArr.length) {
tagsArr.forEach((tag, i) => {
if (tag !== '__name__') {
host += `${tag}="${queryItem.metric[tag]}",`
}
})
if (host.endsWith(',')) {
host = host.substr(0, host.length - 1)
}
if (queryItem.metric.__name__) {
2021-03-19 18:52:19 +08:00
host += '}'
}
2021-03-19 18:52:19 +08:00
if (!host || host === '') {
host = pen.data.expressArr[innerPos]
}
2021-03-19 18:52:19 +08:00
// 处理legend别名
let alias = this.dealLegendAlias(host, pen.data.legends[innerPos])
if (!alias || alias === '') {
alias = host
}
queryItem.legend = { name: host + '-' + pen.data.legends[innerPos] + '-' + resIndex, alias: alias }
queryItem.showValue = getMetricTypeValue(queryItem.values, type)
// 图表中每条线的名字,去掉最后的逗号与空格:metric名称, 标签1=a,标签2=c
}
})
2021-02-05 10:44:07 +08:00
}
2021-03-19 18:52:19 +08:00
})
}
return res
},
dealLegendAlias: function (legend, expression) {
if (/\{\{.+\}\}/.test(expression)) {
const labelValue = expression.replace(/(\{\{.+?\}\})/g, function (i) {
const label = i.substr(i.indexOf('{{') + 2, i.indexOf('}}') - i.indexOf('{{') - 2)
const reg = new RegExp(label + '=".+?"')
let value = null
if (reg.test(legend)) {
const find = legend.match(reg)[0]
value = find.substr(find.indexOf('"') + 1, find.lastIndexOf('"') - find.indexOf('"') - 1)
}
2021-03-19 18:52:19 +08:00
return value || label
})
return labelValue
} else {
return expression
}
},
// 获取project Info
getProjectData (n) {
// 获取projectInfo
this.projectInfo.loading = true
this.$get('project/info', { id: n.id }).then(response => {
if (response.code === 200) {
this.projectInfo.loading = false
this.projectInfo = { ...this.projectInfo, ...response.data.basic, moduleMum: response.data.module.length }
this.projectInfo.total = this.projectInfo.alertStat[0] + this.projectInfo.alertStat[1] + this.projectInfo.alertStat[2]
if (!this.projectInfo.total) {
this.projectInfo.total = 0
}
}
2021-03-19 18:52:19 +08:00
})
},
// Severity Label
returnSeverityLabel (key) {
return this.$CONSTANTS.alertMessage.severityData.find(s => { return s.value == key }).label
},
// 获取module
getModule () {
this.projectInfo.loading = true
this.$get('project/info', { id: this.obj.id }).then(response => {
if (response.code === 200) {
this.projectInfo.loading = false
this.projectInfo = { ...this.projectInfo, ...response.data.basic, moduleMum: response.data.module.length }
this.allModules = response.data.module
this.modulesDiff()
}
})
},
getNodesArr () {
if (!getTopology(this.topologyIndex)) return
this.nodesArr = getTopology(this.topologyIndex).data.pens.filter(item => {
if (!item.data) {
item.data = {
moduleId: '',
moduleName: '',
show: false,
error: false,
expressArr: []
}
}
2021-03-19 18:52:19 +08:00
return item.type === 0
})
// this.nodesArr=this.nodesArr.map(item=>{
// if(!item.data){
// item.data={
// moduleId:'',
// moduleName:'',
// show:false,
// error:false,
// expressArr:[],
// }
// }
// return {
// rect:item.rect,
// data:item.data
// }
// });
// 打开动画 是否更新顶部图标
this.nodesArr = JSON.parse(JSON.stringify(this.nodesArr))
},
modelTopClick (item, index) {
2021-03-19 18:52:19 +08:00
},
// 摘除已选择的module
modulesDiff (data) {
this.modules = this.allModules
if (getTopology(this.topologyIndex).data.pens) {
getTopology(this.topologyIndex).data.pens.forEach(item => {
if (item.type == 0) {
this.modules = this.modules.filter(item1 => item.data.moduleId !== item1.id)
}
})
2021-03-19 18:52:19 +08:00
}
if (data && data.data && data.data.moduleId) {
this.modules.unshift({ id: data.data.moduleId, name: data.data.moduleName })
}
},
2021-03-19 18:52:19 +08:00
// 显示module的工具
showNodeTools (index, pen) {
this.nodesArr.forEach((item, i) => {
item.data.show = i === index
})
},
2021-03-19 18:52:19 +08:00
// 具体内容点击
nodeTools (node, tool) {
this.moduleId = node.data.moduleId
if (tool.id === 'total') {
this.popDataShowUpdate('', false, node)
return
}
setTimeout(() => {
this.popDataShowUpdate(tool.id, false, node)
}, 100)
},
2021-03-19 18:52:19 +08:00
popDataShowUpdate (key, flag, node) { // key 显示对应的弹窗 flag是否不显示工具栏
this.popDataShow = {
endpoint: false,
asset: false,
total: false,
other: false,
info: false,
alert: false,
main: false
}
if (key === 'total') {
this.chartDataInfo = { ...node.data, ...this.chartGetData.find(item => item.id === node.id) }
}
this.$nextTick(() => {
this.popDataShow[key] = true
})
if (flag) { // 处理关闭后 缩放后显示工具按钮的问题
this.moduleId = ''
this.showNodeTools('')
}
if (key === 'asset' || key === 'alert' || key === 'endpoint') {
this.showNodeTools('')
}
},
2021-03-19 18:52:19 +08:00
/* topology 方法 */
onDrag (event, node) {
this.dragFlag = false
setTimeout(() => {
this.dragFlag = true
}, 100)
event.dataTransfer.setData('Text', JSON.stringify({ ...node.data, data: { imageId: node.data.imageId } }))
},
dragFlagChange (node) {
getTopology(this.topologyIndex).addNode(
{
...node.data,
rect: {
...node.data.rect,
x: this.$refs['topology-canvas' + this.topologyIndexF].offsetWidth / 2 - 50,
y: this.$refs['topology-canvas' + this.topologyIndexF].offsetHeight / 2 - 50
},
data: { imageId: node.data.imageId }
})
setTimeout(() => {
this.dragFlag = true
}, 100)
},
onMessage (event, data, e) {
// console.log('onMessage',event,data);
// console.log(getTopology(this.topologyIndex))
// this.notModuleIDArr=[];
if (data) {
this.notModuleIDArr.forEach(item => {
if (item.id === data.id) {
item.rect = data.rect
}
})
}
if (!Array.isArray(data) && data) { // 判断不是数组 提前个data配置好节点属性
if (data.type === 0 && !data.data.moduleId) {
data.data = {
...data.data,
moduleId: '',
moduleName: '',
show: false,
error: false,
animatePlay: false,
fillStyle: data.fillStyle,
strokeStyle: data.strokeStyle,
gradientColor: '#bae7ff',
gradientType: 0,
lineWidth: this.nodeDefaultWidth(data.name),
iconToolState: true,
// chart 配置项
valueMapping: [{
color: {
line: '#000000',
fill: '#ffffff',
text: '#000000'
},
value: 'base',
animateType: 'base',
level: 0,
base: true
}],
valueMappingSort: 'asc',
expressArr: [''],
legends: [''],
tooltipShow: true,
panelName: 'topologyName',
unit: 2,
type: 'line',
displayChart: true,
aggregation: 'last',
title: '',
url: ''
}
2021-03-19 18:52:19 +08:00
} else if (data.type == 1 && !data.data) {
// 连线是否自动计算锚点
// data.manualCps=true;
data.animateColor = '#FA901C'
data.data = {
animatePlay: false,
strokeStyle: data.strokeStyle,
animateColor: data.animateColor,
arrowColor: '#00000',
fromArrowColor: '#00000',
toArrowColor: '#00000',
lineWidth: 1,
// chart 配置项
valueMapping: [{
color: {
line: '#000000',
fill: '#ffffff',
text: '#000000'
},
value: 'base',
animateType: 'base',
level: 0,
base: true
}],
valueMappingSort: 'asc', /* desc */
expressArr: [''],
legends: [''],
tooltipShow: true,
panelName: 'topologyName',
unit: 2,
type: 'line',
displayChart: true,
aggregation: 'last',
title: '',
moduleName: '',
url: ''
}
}
2021-03-19 18:52:19 +08:00
if (data.type === 0 || data.type === 1) {
data.lineWidth = data.data.lineWidth
}
}
2021-03-19 18:52:19 +08:00
switch (event) {
case 'moveInNode':
case 'moveInLine':
if (this.timer3) {
clearTimeout(this.timer3)
this.timer3 = null
}
this.chartData = { ...data.data, ...this.chartGetData.find(item => item.id === data.id) }
this.tooltipPosition.show = false
setTimeout(() => {
this.tooltipPosition.show = true
const ePosition = window.ePosition
const boxWidth = document.getElementsByClassName('page')[0].offsetWidth
const boxHeight = document.getElementsByClassName('page')[0].offsetHeight
this.tooltipPosition.left = ePosition.layerX + 20
this.tooltipPosition.top = ePosition.layerY
this.$nextTick(() => {
if (this.$refs.topoTooltip) {
if ((boxWidth / 2) > ePosition.layerX) {
this.tooltipPosition.left = ePosition.layerX + 20
} else {
this.tooltipPosition.left = ePosition.layerX - 20 - this.$refs.topoTooltip.offsetWidth
}
if (boxHeight > (ePosition.layerY + this.$refs.topoTooltip.offsetHeight)) {
this.tooltipPosition.top = ePosition.layerY
} else {
this.tooltipPosition.top = ePosition.layerY - this.$refs.topoTooltip.offsetHeight
}
2021-02-02 19:24:21 +08:00
}
2021-03-19 18:52:19 +08:00
})
}, 100)
break
case 'moveOutNode':
case 'moveOutLine':
// this.tooltipPosition.show=false;
// return
if (!this.timer3) {
this.timer3 = setTimeout(() => {
this.tooltipPosition.show = false
this.timer3 = null
}, 50)
} else {
clearTimeout(this.timer3)
this.timer3 = setTimeout(() => {
this.tooltipPosition.show = false
this.timer3 = null
}, 50)
}
break
}
// 右侧输入框编辑状态时点击编辑区域其他元素onMessage执行后才执行onUpdateProps方法通过setTimeout让onUpdateProps先执行
setTimeout(() => {
switch (event) {
case 'node':
case 'addNode':
this.modulesDiff(data)
if (data.data.expressArr.length === 0 && event !== 'node') {
data.data.expressArr.push('')
data.data.legends.push('')
}
this.props = {
node: data,
line: null,
multi: false,
expand: this.props.expand,
nodes: null,
locked: data.locked,
pen: data,
pens: null
}
this.$nextTick(() => {
if (this.$refs.CanvasProps) {
if (this.penId !== data.id) {
this.$refs.CanvasProps.tab = '1'
}
2021-03-19 18:52:19 +08:00
this.penId = data.id
}
})
break
case 'line':
case 'addLine':
this.props = {
node: null,
line: data,
multi: false,
nodes: null,
locked: data.locked,
pen: data,
pens: null
}
this.$nextTick(() => {
if (this.$refs.CanvasProps) {
if (this.penId !== data.id) {
this.$refs.CanvasProps.tab = '1'
}
2021-03-19 18:52:19 +08:00
this.penId = data.id
}
2021-03-19 18:52:19 +08:00
})
break
case 'multi':
this.props = {
node: null,
line: null,
multi: true,
nodes: data.length > 1 ? data : null,
locked: this.getLocked({ nodes: data }),
pen: null,
pens: data.length > 1 ? data : null
}
break
case 'space':
this.penId = undefined
this.props = {
node: null,
line: null,
multi: false,
nodes: null,
locked: false,
pen: null,
pens: null
}
break
case 'moveOut':
break
case 'moveNodes':
case 'resizeNodes':
if (data.length > 1) {
this.props = {
node: null,
line: null,
multi: true,
nodes: data,
locked: this.getLocked({ nodes: data }),
pen: null,
pens: null
}
2021-03-19 18:52:19 +08:00
} else {
this.props = {
node: data[0],
line: null,
multi: false,
nodes: null,
locked: false,
pen: data[0],
pens: null
}
2021-03-19 18:52:19 +08:00
}
break
case 'resize': {
const domRect = document.getElementById('topology-canvas' + this.topologyIndexF).getBoundingClientRect()
this.toolShow.attrCord = [domRect.width - 350, 0]
this.toolShow.height = domRect.height
if (getTopology(this.topologyIndex)) {
getTopology(this.topologyIndex).canvasPos = domRect
}
break
}
2021-03-19 18:52:19 +08:00
case 'scale': {
if (this.$refs.topTool) {
this.$refs.topTool.option.scale = data
}
break
}
2021-03-19 18:52:19 +08:00
case 'locked': {
this.props = {
node: null,
line: null,
multi: false,
nodes: null,
locked: false,
pen: null,
pens: null
}
break
}
2021-03-19 18:52:19 +08:00
case 'delete': {
this.props = {
node: null,
line: null,
multi: false,
nodes: null,
locked: false,
pen: null,
pens: null
}
2021-03-19 18:52:19 +08:00
break
}
}
2021-03-19 18:52:19 +08:00
switch (event) {
case 'node':
case 'line':
case 'space':
case 'scale':
case 'translate':
this.moduleId = ''
this.showNodeTools('')
this.popDataShowUpdate('', false)
if (!this.editTopologyFlag) {
getTopology(this.topologyIndex)
}
2021-03-19 18:52:19 +08:00
break
}
switch (event) {
case 'space':
case 'scale':
case 'translate':
this.getNodesArr()
// if(!this.timer2){
// this.timer2=setTimeout(()=>{
// this.getNodesArr();
// this.timer2=null
// },300)
// }else{
// clearTimeout(this.timer2);
// this.timer2=setTimeout(()=>{
// this.getNodesArr();
// this.timer2=null
// },300)
// }
break
}
2021-03-19 18:52:19 +08:00
}, 0)
},
2021-03-19 18:52:19 +08:00
getLocked (data) {
let locked = true
if (data.nodes && data.nodes.length) {
for (const item of data.nodes) {
if (!item.locked) {
locked = false
break
}
}
}
if (locked && data.lines) {
for (const item of data.lines) {
if (!item.locked) {
locked = false
break
}
}
}
2021-03-19 18:52:19 +08:00
return locked
},
2021-03-19 18:52:19 +08:00
onUpdateProps (node) {
// 如果是node属性改变需要传入node重新计算node相关属性值
// 如果是line属性改变无需传参
getTopology(this.topologyIndex).updateProps(node)
},
2021-03-19 18:52:19 +08:00
handleAvatarSuccess () {
2021-03-19 18:52:19 +08:00
},
beforeAvatarUpload (file, fileList) {
const this_ = this
const isJPG = (file.raw.type === 'image/jpeg' || file.raw.type === 'image/png')
2021-03-19 18:52:19 +08:00
if (!isJPG) {
this.$message.error(this_.$t('project.topology.imgFormat'))
return false
}
// if (!isLt2M) {
// this.$message.error( this_.$t('project.topology.imgSize'));
// return false;
// }
const isSize = new Promise(function (resolve, reject) {
const width = 100
const height = 100
const _URL = window.URL || window.webkitURL
const img = new Image()
img.onload = function () {
const valid = img.width > width && img.height > height
valid ? resolve() : reject()
}
2021-03-19 18:52:19 +08:00
img.src = _URL.createObjectURL(file.raw)
}).then(() => {
if (isJPG) {
this.file = file.raw
}
return file.raw
}, () => {
this.$message.error(this_.$t('project.topology.imgMeasure'))
return Promise.reject()
})
return false
},
2021-03-19 18:52:19 +08:00
toolShowChange (attr) {
this.toolShow[attr] = !this.toolShow[attr]
},
/* topology 方法 */
end (v) {
2021-03-19 18:52:19 +08:00
},
/* tools 方法 */
imgUpload () {
const this_ = this
if (!this.uploadPic.unit) {
this.$message({
message: this_.$t('project.topology.unitError'),
type: 'error'
})
return
}
if (!this.file) {
this.$message({
message: this_.$t('project.topology.imgError'),
type: 'error'
})
return
}
this.upload()
},
upload () {
const form = new FormData()
form.append('file', this.file)
if (this.uploadPic.name) {
form.append('name', this.uploadPic.name)
} else {
form.append('name', this.file.name.substring(0, this.file.name.lastIndexOf('.')))
}
form.append('unit', this.uploadPic.unit)
this.$post('/project/topo/icon', form, { 'Content-Type': 'multipart/form-data' }).then(res => {
if (res.code == 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.saveSuccess') })
this.uploadPicShow = false
this.dealImg(`/project/topo/icon/${res.data.id}`).then((data) => {
const group = this.tools.find(tool => tool.group === this.uploadPic.unit)
if (group) {
group.children.push({
...imageTemp,
data: {
...imageTemp.data,
text: res.data.imageName,
image: data,
imageId: res.data.id,
unit: this.uploadPic.unit
}
})
} else {
this.tools.push({
group: this.uploadPic.unit,
children: [{
...imageTemp,
data: {
...imageTemp.data,
text: res.data.imageName,
image: data,
imageId: res.data.id,
unit: this.uploadPic.unit
}
}]
})
}
})
} else {
this.$message.error(res.msg)
}
2021-03-19 18:52:19 +08:00
})
},
delImg (item) {
this.$delete('/project/topo/icon?ids=' + item.data.imageId).then(res => {
if (res.code == 200) {
this.$message({ duration: 2000, type: 'success', message: this.$t('tip.deleteSuccess') })
this.addNodeInit()
} else {
this.$message.error(res.msg)
}
2021-03-19 18:52:19 +08:00
})
},
addNodeInit (imgidList) {
if (!this.fromOverView) {
this.$get('/project/topo/icon').then(res => {
this.imgageLoading = true
// this.tools[1].children=[];
const imgArr = []
const promiseArr = []
res.data.list.forEach((item, index) => {
item.imageName = item.name
delete item.name
promiseArr.push(this.dealImg(`/project/topo/icon/${item.id}`))
imgArr.push({ ...item })
})
Promise.all(promiseArr).then((res2) => {
this.iconArray = [...res.data.list]
this.iconArray.forEach((item, index) => {
item.image = res2[index]
const group = this.tools.find(tool => tool.group === item.unit)
if (group) {
group.children.push({
...imageTemp,
2021-03-19 18:52:19 +08:00
data: {
...imageTemp.data,
2021-03-19 18:52:19 +08:00
text: item.imageName,
image: res2[index],
imageId: item.id,
unit: item.unit
}
})
2021-03-19 18:52:19 +08:00
} else {
this.tools.push({
2021-03-19 18:52:19 +08:00
group: item.unit,
children: [{
...imageTemp,
2021-03-19 18:52:19 +08:00
data: {
...imageTemp.data,
2021-03-19 18:52:19 +08:00
text: item.imageName,
image: res2[index],
imageId: item.id,
unit: item.unit
}
2021-03-19 18:52:19 +08:00
}]
})
}
})
2021-03-19 18:52:19 +08:00
this.imgInit = true
})
})
} else {
this.imgageLoading = true
const promiseArr = []
imgidList.forEach((item, index) => {
if (item.data.imageId) {
promiseArr.push(this.dealImg(`/project/topo/icon/${item.data.imageId}`))
} else {
promiseArr.push('')
}
})
2021-03-19 18:52:19 +08:00
Promise.all(promiseArr).then(res2 => {
this.iconArray = imgidList.map(item => {
return {
id: item.data.imageId
}
})
2021-03-19 18:52:19 +08:00
this.iconArray.forEach((item, index) => {
if (item.id) {
item.image = res2[index]
}
2021-02-04 17:06:04 +08:00
})
2021-03-19 18:52:19 +08:00
this.imgInit = true
})
}
},
2021-03-19 18:52:19 +08:00
dealImg (url) {
// 处理后端传过来的图片流乱码问题
if (url) {
return new Promise((resolve, reject) => {
this.$axios
.get(url, {
responseType: 'arraybuffer'
})
.then(res => {
return ('data:image/jpeg;base64,' + btoa(new Uint8Array(res.data).reduce((data, byte) => data + String.fromCharCode(byte), '')))
})
.then(data => {
resolve(data)
// changeImage(data,(img)=>{
// resolve(img)
// })
})
.catch(err => {
})
})
}
},
2021-03-19 18:52:19 +08:00
delPen (obj) { // 删除元素
getTopology(this.topologyIndex).delete(obj)
this.props = {
node: null,
line: null,
multi: false,
nodes: null,
locked: false,
pen: null,
pens: null
}
},
2021-03-19 18:52:19 +08:00
editTopology (val) {
this.editTopologyFlag = true
setTimeout(() => {
getTopology(this.topologyIndex).lock(0)
getTopology(this.topologyIndex).data.pens.forEach((item, index) => { // 停止动画 以及赋值默认data
if (item.animatePlay) {
item.stopAnimate()
}
if (!item.data.expressArr.length) {
item.data.expressArr = ['']
item.data.legends = ['']
}
item.animateType = item.data.animateType
if (item.type === 0) {
item.fillStyle = item.data.fillStyle
item.strokeStyle = item.data.strokeStyle
item.animatePlay = false
item.fontColor = '#000000'
item.gradientType = item.data.gradientType ? item.data.gradientType : 0
if (!item.data.gradientColor) {
item.data.gradientType = 0
item.data.gradientColor = '#bae7ff'
}
2021-03-19 18:52:19 +08:00
if (item.data.gradientType === 0) {
item.bkType = 0
} else {
item.bkType = 1
}
2021-03-19 18:52:19 +08:00
} else if (item.type === 1) {
item.animateColor = item.data.animateColor
item.strokeStyle = item.data.strokeStyle
item.arrowColor = item.data.arrowColor
item.fromArrowColor = item.data.arrowColor
item.toArrowColor = item.data.arrowColor
item.animatePlay = false
item.fontColor = '#000000'
}
})
2021-03-19 18:52:19 +08:00
const domRect = document.getElementById('topology-canvas' + this.topologyIndexF).getBoundingClientRect()
this.toolShow.attrCord = [domRect.width - 350, 0]
this.toolShow.height = domRect.height
getTopology(this.topologyIndex).canvasPos = domRect
getTopology(this.topologyIndex).caches = {
index: 0,
list: [JSON.parse(JSON.stringify(getTopology(this.topologyIndex).data))]
}
})
},
2021-03-19 18:52:19 +08:00
animateCanvas () {
getTopology(this.topologyIndex).render()
getTopology(this.topologyIndex).animate()
},
2021-03-19 18:52:19 +08:00
refreshTopology () {
// canvas.open()
},
2021-03-19 18:52:19 +08:00
dropdownClick () {
2021-02-02 10:30:45 +08:00
2021-03-19 18:52:19 +08:00
},
2021-02-02 10:30:45 +08:00
2021-03-19 18:52:19 +08:00
changeTopologyOpt (val, key) {
// this.topologyData.data[key]=this.colorRGBtoHex(val);
// getTopology(this.index).data[key]=val;
// getTopology(this.index).render();
const dataOption = getTopology(this.topologyIndex).data
dataOption[key] = this.lineName
getTopology(this.topologyIndex).render()
},
notModuleIDArrChange (id) {
this.notModuleIDArr = this.notModuleIDArr.filter(item => item.id !== id)
},
// 保存
saveTopology () {
const topologyData = getTopology(this.topologyIndex).pureData()
let flag = true
const arr = []
this.notModuleIDArr = []
topologyData.pens.forEach(item => {
if (item.type === 0 && ((!item.data) || (item.data && !item.data.moduleId))) {
arr.push(item)
this.props = {
node: null,
line: null,
multi: false,
nodes: null,
locked: false,
pen: null,
pens: null
}
2021-03-19 18:52:19 +08:00
flag = false
}
2021-03-19 18:52:19 +08:00
})
if (!flag) {
this.notModuleIDArr = arr
}
if (flag) {
this.editTopologyFlag = false
topologyData.rule = false
topologyData.grid = false
topologyData.gridSize = 10
topologyData.pens.forEach(item => {
item.animatePlay = item.data.animatePlay
item.data.animateType = item.animateType
if (item.type === 0 && JSON.stringify(item.data.imageId)) {
item.image = ''
item.animateFrames = []
item.animateReady = null
delete item.img
delete item.lastImage
2021-02-25 10:35:05 +08:00
}
2021-03-19 18:52:19 +08:00
item.data.expressArr = item.data.expressArr.filter((expression, i) => {
if (!expression) {
item.data.legends.splice(i, 1)
return false
} else {
return true
}
2021-03-19 18:52:19 +08:00
})
})
if (this.penToolTipScale == getTopology(this.topologyIndex).data.scale) {
getTopology(this.topologyIndex).data.scale = this.oldScale
}
this.$put('/project/topo', { topo: JSON.stringify(topologyData), projectId: this.projectInfo.id }).then(res => {
this.prevent_opt.save = false
if (res.code === 200) {
this.$message({
2021-03-19 18:52:19 +08:00
message: this.$t('tip.saveSuccess'),
type: 'success'
})
this.$nextTick(() => {
getTopology(this.topologyIndex).lock(1)
const domRect = document.getElementById('topology-canvas' + this.topologyIndexF).getBoundingClientRect()
this.toolShow.attrCord = [domRect.width - 350, 0]
this.toolShow.height = domRect.height
getTopology(this.topologyIndex).canvasPos = domRect
this.reload()
})
}
}).catch(res => {
this.prevent_opt.save = false
this.$message({
2021-03-19 18:52:19 +08:00
message: res.msg,
type: 'error'
})
2021-03-19 18:52:19 +08:00
})
} else {
this.$message({
showClose: true,
message: this.$t('project.topology.selMod'),
type: 'warning'
})
}
},
// 取消
cancelTopology () {
this.editTopologyFlag = false
this.$nextTick(() => {
getTopology(this.topologyIndex).lock(1)
const domRect = document.getElementById('topology-canvas' + this.topologyIndexF).getBoundingClientRect()
this.toolShow.attrCord = [domRect.width - 350, 0]
this.toolShow.height = domRect.height
getTopology(this.topologyIndex).canvasPos = domRect
})
this.reload()
},
// 预览
previewTopology () {
const data = JSON.parse(JSON.stringify(getTopology(this.topologyIndex).data))
data.pens.forEach((item) => {
item.animatePlay = item.data.animatePlay
})
this.topoPrevData = JSON.parse(JSON.stringify(data))
this.topoPrevData.rule = false
this.topoPrevData.grid = false
this.previewShow = true
},
2021-02-09 16:27:22 +08:00
2021-03-19 18:52:19 +08:00
// 联动 project
changeProjectTitle () {
const data = getTopology(this.topologyIndex).data
this.topologyInfo = {
fontSize: data.data.fontSize,
align: data.data.align,
fontColor: data.data.fontColor,
opacity: data.data.opacity,
name: data.name
}
},
/* tools 方法 */
winResize () {
setTimeout(() => {
const domRect = document.getElementById('topology-canvas' + this.topologyIndex).getBoundingClientRect()
// this.toolShow.attrCord=[domRect.width-350,0];
// this.toolShow.height=domRect.height;
getTopology(this.topologyIndex).canvasPos = domRect
if (this.fromOverView) {
getTopology(this.topologyIndex).open(this.oldTopologyData)
2021-02-09 16:27:22 +08:00
}
2021-03-19 18:52:19 +08:00
let flag = false
2021-03-19 18:52:19 +08:00
const position = {
x: this.$refs['topology-canvas' + this.topologyIndexF].offsetWidth,
y: this.$refs['topology-canvas' + this.topologyIndexF].offsetHeight
2021-02-05 15:26:27 +08:00
}
2021-03-19 18:52:19 +08:00
getTopology(this.topologyIndex).data.pens.forEach(item => {
if (flag) {
return
}
2021-03-19 18:52:19 +08:00
if (item.rect.ex > position.x || item.rect.ey > position.y) {
getTopology(this.topologyIndex).fitView(20)
flag = true
}
})
getTopology(this.topologyIndex).centerView(20)
this.getNodesArr()
}, 100)
},
canvasMove (e) { // 画布上的移动 确定tooltip的位置
if (this.tooltipPosition.show) {
return
}
window.ePosition = e
},
2021-03-19 18:52:19 +08:00
tooltipOver () {
clearTimeout(this.timer3)
this.timer3 = null
},
tooltipOut () {
this.timer3 = setTimeout(() => {
this.tooltipPosition.show = false
this.timer3 = null
}, 50)
},
nodeDefaultWidth (nodeName) {
switch (nodeName) {
case 'myCube':
case 'rectangleImg':
return 0
default:
return 1
}
},
previewBeforeClose (done) {
this.$emit('changeTopologyIndexF')
done()
},
selpopIs (pen, state) { // 判断是否有图表
let flag = true
if (state.id === 'other') {
flag = false
}
if (state.id === 'total' && pen.data.expressArr.length === 0) {
flag = false
}
return flag
},
modelPopError (pen, state) {
if (item.id === 'asset' && this.activeModelItem.assetError) {
return true
}
if (item.id === 'alert' && this.activeModelItem.alertError) {
return true
}
2021-03-19 18:52:19 +08:00
if (item.id === 'endpoint' && this.activeModelItem.endpointError) {
return true
2021-02-04 17:06:04 +08:00
}
2021-03-19 18:52:19 +08:00
return false
},
redoIndexChange (index) {
this.redoIndex = index
},
querySearch (queryString, cb) {
const restaurants = this.unitArr
const results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
// 调用 callback 返回建议列表的数据
cb(results)
},
createFilter (queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)
}
},
uploadPicChange () {
this.unitArr = []
this.tools.forEach((item, index) => {
if (index > 0) {
this.unitArr.push({
value: item.group
})
}
})
this.uploadPic.name = ''
this.uploadPic.unit = ''
this.file = null
this.uploadPicShow = true
},
zoomMap (num) {
getTopology(this.topologyIndex).scaleTo(getTopology(this.topologyIndex).data.scale + num)
},
penToBottom () {
getTopology(this.topologyIndex).bottom()
}
},
destroyed () {
if (getTopology(this.topologyIndex)) {
getTopology(this.topologyIndex).destroy()
setTopology(this.topologyIndex, null)
}
2021-03-19 18:52:19 +08:00
if (document.getElementById('topology-canvas' + this.topologyIndexF)) {
document.getElementById('topology-canvas' + this.topologyIndexF).removeEventListener('mousemove', this.canvasMove)
}
window.removeEventListener('resize', this.winResize)
}
2021-03-19 18:52:19 +08:00
}
</script>
2021-02-02 10:30:45 +08:00
<style lang="scss">
.el-dropdown-menu {
.project-topology-add-node {
2021-02-02 10:30:45 +08:00
.el-collapse-item__header {
padding: 0 10px;
background-color: #ffffff;
2021-02-02 19:24:21 +08:00
height: 32px;
2021-02-02 10:30:45 +08:00
}
.el-collapse-item__header.is-active {
2021-02-02 10:30:45 +08:00
background: #F6F6F6;
ont-family: Roboto-Bold;
font-size: 14px;
color: #FA901C;
font-weight: 700;
el-collapse-item__arrow {
2021-02-02 10:30:45 +08:00
color: #666;
}
}
2021-02-02 10:30:45 +08:00
.el-collapse-item__wrap {
padding: 0 10px;
background-color: #ffffff;
}
2021-02-02 10:30:45 +08:00
.el-collapse-item__content {
padding: 12px 0px;
display: flex;
flex-wrap: wrap;
/*justify-content: space-around;*/
}
2021-02-02 10:30:45 +08:00
.el-card__body {
padding: 0;
height: 100%;
}
2021-02-02 10:30:45 +08:00
.handle {
position: absolute;
z-index: 2;
}
2021-02-02 10:30:45 +08:00
.buttons {
padding: 12px;
display: inline-block;
position: relative;
vertical-align: middle;
width: 26px;
2021-02-02 10:30:45 +08:00
.delIcon {
position: absolute;
width: 16px;
height: 16px;
border-radius: 10px;
font-size: 12px;
line-height: 16px;
text-align: center;
background: red;
right: -8px;
top: -8px;
color: #fff;
display: none;
cursor: pointer;
}
a {
display: inline-block;
color: #314659;
width: 26px;
height: 26px;
text-align: center;
text-decoration: none !important;
cursor: pointer;
line-height: 26px;
.iconfont {
font-size: 24px;
}
img {
max-width: 26px;
max-height: 26px;
}
&:hover {
color: #1890ff;
}
}
.upload-icon-box {
.el-icon-plus {
font-size: 14px;
margin-bottom: 10px;
}
2021-02-02 10:30:45 +08:00
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
}
}
2021-02-02 10:30:45 +08:00
.buttons:hover {
.delIcon {
/*display: inline-block;*/
}
}
}
.avatar-uploader {
2021-02-02 10:30:45 +08:00
line-height: 30px;
.el-icon-plus {
2021-02-02 10:30:45 +08:00
font-size: 12px;
color: #FA901C;
margin: 0 8px 0 15px;
}
.el-upload--picture-card {
2021-02-02 10:30:45 +08:00
width: 100%;
height: 100%;
border: none;
color: #666;
font-size: 14px;
line-height: 30px;
text-align: left;
}
.el-upload--picture-card:hover, .el-upload:focus {
2021-02-02 10:30:45 +08:00
color: #666;
}
}
.avatar-uploader:active el-upload--picture-card {
2021-02-02 10:30:45 +08:00
color: #DB8B8B;
}
.avatar-uploader:active .el-upload--picture-card:hover, .avatar-uploader:active .el-upload:focus {
2021-02-02 10:30:45 +08:00
color: #DB8B8B;
}
}
</style>
<style scoped>
@keyframes model-error-animation {
0% {
transform: scale(0.7);
}
2021-02-02 10:30:45 +08:00
50% {
transform: scale(1);
}
100% {
transform: scale(0.7);
}
}
2021-02-02 10:30:45 +08:00
@keyframes model-icon-animation {
0% {
transform: scale(1.2) translateX(1px);
}
2021-02-02 10:30:45 +08:00
50% {
transform: scale(0.9) translateX(0px);
}
100% {
transform: scale(1.2) translateX(1px);
}
}
2021-02-02 10:30:45 +08:00
.nz-icon-shuidi {
position: absolute;
font-size: 48px;
2021-02-02 10:30:45 +08:00
color: rgba(190, 233, 222, 0.45);
border-radius: 50%;
height: 48px;
width: 48px;
line-height: 48px;
}
2021-02-02 10:30:45 +08:00
.model-error.nz-icon-shuidi {
color: #FADED7;
animation: model-error-animation .6s infinite ease-in-out;
2021-02-02 10:30:45 +08:00
animation-direction: normal;
}
2021-02-02 10:30:45 +08:00
.model-error-active.nz-icon-shuidi {
color: #FADED7;
}
2021-02-02 10:30:45 +08:00
.nz-icon-model {
color: #23BF9A;
position: absolute;
top: -4px;
left: 15px;
font-size: 18px;
line-height: 48px;
}
2021-02-02 10:30:45 +08:00
.model-error .nz-icon-model {
color: #EC7F66;
animation: model-icon-animation .6s infinite ease-in-out;
2021-02-02 10:30:45 +08:00
animation-direction: normal;
}
2021-02-02 10:30:45 +08:00
.model-error-active .nz-icon-model {
color: #EC7F66;
}
2021-02-02 10:30:45 +08:00
.scaleTool-enter-active {
animation: scaleTool-in .15s;
}
2021-02-02 10:30:45 +08:00
.scaleTool-leave-active {
animation: scaleTool-in .15s reverse;
}
2021-02-02 10:30:45 +08:00
@keyframes scaleTool-in {
2021-02-02 10:30:45 +08:00
from {
top: 0px;
left: 0px;
transform: scale(0.5);
}
}
2021-02-02 10:30:45 +08:00
.moduleIdRect{
/*border: 4px dashed #FA901C;*/
}
.module-rect-top{
border-top: 4px dashed #ff8c0a;
position: absolute;
}
.module-rect-right{
border-right: 4px dashed #ff8c0a;
position: absolute;
}
.module-rect-bottom{
border-bottom: 4px dashed #ff8c0a;
position: absolute;
}
.module-rect-left{
border-left: 4px dashed #ff8c0a;
position: absolute;
}
.network-pop .nz-icon-hexagonBorder{
position: absolute;
font-size: 48px;
color: #84d5c2;
height: 48px;
width: 48px;
line-height: 48px;
}
2021-02-02 10:30:45 +08:00
.network-pop .nz-icon-hexagonBorder:hover {
transform: scale(1.1);
color: #4BB49B;
}
2021-02-02 10:30:45 +08:00
.network-pop .nz-icon-hexagonBorder.error-model-stat {
color: #F5BAAC;
}
2021-02-02 10:30:45 +08:00
.network-pop .nz-icon-hexagonBorder.error-model-stat:hover {
color: #EC7F66;
}
2021-02-02 10:30:45 +08:00
.network-pop .nz-icon-liubianxing {
color: #e2f3ef;
font-size: 44px;
position: absolute;
top: 1px;
left: 2px;
transform: scale(0.95);
z-index: 0;
}
2021-02-02 10:30:45 +08:00
.network-pop .error-model-stat .nz-icon-liubianxing {
color: #FADED7;
}
2021-02-02 10:30:45 +08:00
.network-pop .nz-icon.noMove {
position: absolute;
left: 14px;
font-size: 20px;
color: #27c09c;
}
2021-02-02 10:30:45 +08:00
.network-pop .error-model-stat .nz-icon.noMove {
color: #EC7F66;
}
2021-02-02 10:30:45 +08:00
.network-pop .no-selPop {
2021-02-05 15:26:27 +08:00
color: #999 !important;
}
2021-02-02 10:30:45 +08:00
.network-pop .no-selPop .nz-icon-liubianxing {
color: rgb(218, 218, 218);
}
2021-02-02 10:30:45 +08:00
.network-pop .no-selPop .nz-icon-chart {
color: #999;
}
2021-02-02 10:30:45 +08:00
.network-info {
position: absolute;
right: 0;
top: 50px;
}
2021-02-05 10:44:07 +08:00
.facade-top{
min-height: 138px;
display: flex;
margin: 4px 0;
2021-02-05 10:44:07 +08:00
height: calc(16% - 40px);
font-size: 12px;
z-index: 10;
padding-left: 15px;
2021-02-05 10:44:07 +08:00
}
.facade-top > div{
width: 18%;
min-width: 315px;
background: #FFFFFF;
margin-right: 9px;
padding: 20px;
border: 1px solid #FFFFFF;
box-shadow: 1px 2px 4px 0 rgba(0,0,0,0.12), -1px 1px 9px -1px rgba(205,205,205,0.77);
}
.facade-top-title{
font-size: 16px;
color: #333333;
font-weight: bold;
padding: 5px 0;
}
.facade-top-left{
display: flex;
flex-direction: column;
justify-content: space-around;
}
.special.label{
margin-left: 30px;
}
.facade-top .facade-top-right{
width: auto;
min-width: 100px;
}
.facade-top-right-content{
display: flex;
justify-content: space-around;
justify-items: center;
flex-direction: column;
height: calc(100% - 30px);
align-items:flex-start;
}
.facade-top-right-content > div{
min-width: 84px;
height: 22px;
display: flex;
justify-content: space-between;
color: #fff;
text-align: center;
margin-bottom: 5px;
line-height: 22px;
}
.facade-top-right-content > div > div:last-child{
text-align: center;
border-radius: 0 4px 4px 0;
flex: 1;
height: calc(100% - 2px);
padding: 0 8px;
min-width: 40px;
}
.content-P1-title{
background: #F2866E;
border-radius: 4px 0 0 4px;
width: 40px;
height: 100%;
}
.content-P1-title + div{
border: 1px solid #F4907A;
font-size: 12px;
color: #F4907A;
}
.content-P2-title{
background: #F89984;
border-radius: 4px 0 0 4px;
width: 40px;
height: 100%;
}
.content-P2-title + div{
border: 1px solid #F9A28F;
font-size: 12px;
color: #F9A28F;
}
.content-P3-title{
background: #F7BA78;
border-radius: 4px 0 0 4px;
width: 40px;
height: 100%;
}
.content-P3-title + div{
border: 1px solid #F7BA78;
font-size: 12px;
color: #F7BA78;
}
.right-content-P1{
border: 1px solid ;
}
.align--center{
text-align: center;
}
</style>
<style lang="scss" scoped>
.project-topology-tool {
2021-02-02 10:30:45 +08:00
display: inline-flex;
height: 30px;
}
.el-dropdown-title {
2021-02-02 10:30:45 +08:00
background: #FFFFFF;
border: 1px solid #DEDEDE;
border-radius: 2px;
width: 66px;
height: 28px;
display: inline-block;
line-height: 28px;
.icon-cube {
2021-02-02 10:30:45 +08:00
margin-left: 15px;
}
}
.project-box {
width: 100%;
2021-02-05 10:44:07 +08:00
height: calc(100% - 20px);
margin-top: 10px;
position: relative;
2021-02-05 10:44:07 +08:00
border: 1px solid #eeeeee;
border-radius: 2px;
overflow: hidden;
.pickTime{
margin-top: -13px;
}
.project-title {
2021-02-05 10:44:07 +08:00
height: 34px;
padding-top: 8px;
padding-bottom: 8px;
}
.drag-header {
cursor: move;
background: #1a1a1a;
color: #fff;
}
.left-bottom {
position: absolute;
left: 10px;
bottom: 10px;
}
}
.page {
display: flex;
height: calc(100% - 60px);
width: 100%;
position: relative;
.tools {
width: 300px;
height: 100%;
border: none;
position: absolute;
z-index: 1 !important;
left: 20px;
background-color: #f9f9f9;
.title {
float: left;
}
}
.full {
flex: 1;
overflow: unset !important;
}
.props {
2021-02-02 19:24:21 +08:00
width: 500px;
height: 100%;
border: none;
position: absolute;
z-index: 1 !important;
2021-02-02 10:30:45 +08:00
right: 0;
top: 0;
background: #FFFFFF;
box-shadow: inset 1px 0 0 0 rgba(0, 0, 0, 0.09);
2021-02-04 18:35:53 +08:00
border-radius: 0px;
2021-02-02 10:30:45 +08:00
}
}
.overview-page{
height: 100%;
}
.special-select svg {
2021-02-02 10:30:45 +08:00
width: 75px;
height: 30px;
}
.special-select .el-select.el-select--small {
width: 100%;
}
.special-select /deep/ .el-select-dropdown {
width: 130px !important;
.el-select-dropdown__item {
2021-02-02 10:30:45 +08:00
padding: 0 0 0 10px;
}
}
2021-02-02 10:30:45 +08:00
.special-select /deep/ .el-input.el-input--prefix.el-input--suffix, .line-width /deep/ .el-input.el-input--prefix.el-input--suffix {
border: 1px solid #DCDFE6;
height: 28px;
}
.special-select /deep/ .el-input__inner, .line-width /deep/ .el-input__inner {
display: none;
}
.special-select /deep/ .el-input__prefix, .line-width /deep/ .el-input__prefix {
height: 28px;
line-height: 28px;
color: #899FB7;
width: 100%;
}
.special-select /deep/ .el-input__prefix > div {
width: 100%;
height: 100%;
}
.upload-pic-row{
width: 420px;
margin-bottom: 10px;
.upload-pic-label{
text-align: right;
font-size: 14px;
color: #666666;
letter-spacing: 0;
font-weight: 400;
height: 30px;
line-height: 30px;
padding-right: 8px;
}
/deep/ .el-upload--text{
width: 100%;
.el-upload-dragger{
width: 100%;
}
}
}
.upload-pic-box{
width: 284px;
height: 30px;
text-align: center;
font-size: 14px;
color: #666666;
font-weight: 400;
line-height: 30px;
cursor: pointer;
.el-icon-plus{
color: #FA901C;
}
}
</style>