包含svg元素的创建 元素的拖动 元素间连线 以及连线后的拖动 等
<template>
<div class="contents" id="svgcontents">
<div class="svgDiv">
<svg @touchstart.self="dragStart($event)" @touchmove.stop.prevent.self="dragSvg($event)" id="svgDom" width="100%" height="100%" :viewBox="svgViewBox" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!--<text class="nodeText iconfont" :x="50" :y="120" ></text>-->
<g @touchstart="dragStart($event)" :data-mold="item.type" :data-catalog="item.catalog" @touchmove.prevent="dragDom($event,item.id)" v-for="(item,index) in iData.node" :id="item.id" @click="addClass($event,item)" :class="{selectG : item.id == curent}" >
<!--<image :x="calPosition(item).imgPos[0]" :y="calPosition(item).imgPos[1]" :xlink:href="require('../assets/img/'+item.type+'.png')" height="40" width="40" preserveAspectRatio="xMidYMid meet "/>-->
<!--<foreignObject :x="item.x" :y="item.y" width="30" height="30" style="">
<i class="iconfont"></i>
</foreignObject> -->
<text data-type="icon" class="nodeText iconfont iconText" :x="calPosition(item).imgPos[0]" :y="calPosition(item).imgPos[1]" >{{iconTranscod(item.type)}}</text>
<text data-type="text" class="nodeText nameText" :x="calPosition(item).textPos[0]" :y="calPosition(item).textPos[1]" >{{item.name}}</text>
<circle :cx="calPosition(item).cirLine[0]" :cy="calPosition(item).cirLine[1]" r="4" stroke-width="0" :class="item.onlineStatus == true? 'cir-true' : 'cir-false'"/>
<circle :cx="calPosition(item).cirVer[0]" :cy="calPosition(item).cirVer[1]" r="4" stroke-width="0" :class="item.verificaStatus == true? 'cir-true' : 'cir-false'"/>
</g>
<!--<path @click="test()" d = "M415 75,L75 140" style="stroke: #0000cc; stroke-width: 2px;fill : #ccccff;" marker-end="url(#idArrow)"></path>-->
<path @click="choosePath" class="linkPath" v-for="(item,index) in pathAttrArr" :id="item.start+'_'+item.end" :data-start="item.start" :data-end="item.end" :d = "item.d" style="stroke: #41a7bb; stroke-width: 2px;fill : #41a7bb;" marker-end="url(#idArrow)"></path>
<text class="pathText" :rotate="item.dir" v-for="(item,index) in pathAttrArr" text-anchor="middle" style="font-size:10pt;">
<textPath :xlink:href="'#'+item.start+'_'+item.end" startOffset="50%" >
<tspan >{{item.text}}</tspan>
<input type="text" value="dfdfdd" />
</textPath>
</text>
<!--箭头标志-->
<marker id="idArrow"
viewBox="0 0 20 20" refX="0" refY="10"
markerUnits="strokeWidth" markerWidth="5" markerHeight="18" orient="auto">
<path d="M 0 0 L 20 10 L 0 20 z" fill="#41a7bb" stroke="#41a7bb"/>
</marker>
</svg>
</div>
<!--情景状态切换-->
<van-dropdown-menu>
<van-dropdown-item v-model="sceneVal" :options="sceneOption" @change="sceneChange" />
</van-dropdown-menu>
<!--保存按钮-->
<div v-if="svgStatus == 1" class="btn_group" >
<!--<van-button type="primary" plain size="large" @click="saveData">保存</van-button>-->
<!--<a class="save_btn" @click="saveData">保存 <i class="iconfont"></i></a>-->
<van-button type="default" block>刷新<i class="iconfont"></i></van-button>
<van-button type="default" @click="addNode" block>新增节点</van-button>
<van-button type="default" block @click="saveData">保存</van-button>
</div>
<!--连接线输入框-->
<div v-else-if="svgStatus == 0" class="input-div">
<van-field id="pathtext" type="digit" ref="pathText" size="large" autofocus v-model="pathText" label="" border clearable />
</div>
<!--节点输入框-->
<div v-else-if="svgStatus == 2" class="input-div">
<van-field id="nodetext" ref="nodeText" size="large" autofocus v-model="nodeText" label="" border clearable />
</div>
<!--连线菜单-->
<div v-show="isMenuShow" id="menuDiv" class="menus" :key="menuKey" :style="'top:'+ alertDia.y + 'px; left:'+ alertDia.x +'px;'">
<div id="childLevel" @click="upPathText" class="circle circle1"><i class="iconfont"></i></div>
<div id="delLevel" @click="delPath" class="circle circle2"><i class="iconfont"></i></div>
<div id="textLevel" @click="upPathText" class="circle circle3"><i class="iconfont"></i></div>
</div>
<!--节点菜单-->
<transition name="van-fade" >
<div id="nodeMenu" class="nodeMenu" v-show="nodeMenuShow" :style="'top:'+ alertNode.y + 'px; left:'+ alertNode.x +'px;'">
<!--当前节点状态-->
<div class="nodeStatus">
<!--验证状态-->
<div class="verSta ">
<label> 是否验证:</label>
<label v-if="chooseNodeData.verificaStatus == true" class="lab-true">是</label>
<label v-else class="lab-false">否</label>
</div>
<!-- 上线状态 -->
<div class="lineSta">
<label> 是否在线:</label>
<label v-if="chooseNodeData.onlineStatus == true" class="lab-true">是</label>
<label v-else class="lab-false">否</label>
</div>
</div>
<!--操作按钮-->
<div class="nodeOper">
<div class="operDiv">
<span id="" @click="updateNodeData">修改</span>
<span id="" @click="delNodeData">删除</span>
<span id="" @click="createPathLine">连接</span>
<span id="" @click="verifyNodePop">验证</span>
<span v-if="verifyDef(chooseNodeData.type) " id="" @click="defaultNodePop">缺省值</span>
</div>
</div>
</div>
</transition>
<!--pupup 缺省值设置-->
<van-popup v-model="defaultShow" :style="{ height: '37%',width:'85%'}" >
<div class="defInputDiv">
<!--空调-->
<div v-if="chooseNodeData.type == 'ktyk'" class="kt_default nodeDefault">
<van-field name="defaultData.wd" label="模式">
<template #input>
<van-radio-group v-model="chooseNodeData.default.ktms" >
<van-radio name="1" style="padding:0.1rem 0.05rem">制冷</van-radio>
<van-radio name="2" style="padding:0.1rem 0.05rem">制热</van-radio>
<van-radio name="3" style="padding:0.1rem 0.05rem">除湿</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field name="stepper" label="温度" >
<template #input>
<van-stepper name="温度" v-model="chooseNodeData.default.ktwd" step="2" />
</template>
</van-field>
</div>
<!--窗帘-->
<div v-if="chooseNodeData.type == 'clyk'" class="cl_default nodeDefault">
<van-field name="defaultData.wd" label="开合状态">
<template #input>
<van-radio-group v-model="chooseNodeData.default.clkhzt" >
<van-radio name="1" style="padding:0.1rem 0.05rem">半开</van-radio>
<van-radio name="2" style="padding:0.1rem 0.05rem">全开</van-radio>
<van-radio name="3" style="padding:0.1rem 0.05rem">全关</van-radio>
</van-radio-group>
</template>
</van-field>
</div>
<!--热水器-->
<div v-if="chooseNodeData.type == 'rsqyk'" class="rsq_default nodeDefault">
<van-field name="stepper" label="温度" >
<template #input>
<van-stepper v-model="chooseNodeData.default.rsqwd" step="25" />
</template>
</van-field>
</div>
<!--音响-->
<div v-if="chooseNodeData.type == 'yxyk'" class="rsq_default nodeDefault">
<van-field name="stepper" label="音量">
<template #input>
<van-stepper v-model="chooseNodeData.default.yxyl" step="2" />
</template>
</van-field>
</div>
<!--电视-->
<div v-if="chooseNodeData.type == 'dsyk'" class="ds_default nodeDefault">
<van-field name="chooseNodeData.default.dsxhy" label="信号源">
<template #input>
<van-radio-group v-model="chooseNodeData.default.dsxhy" >
<van-radio name="1" style="padding:0.1rem 0.05rem">HDMI</van-radio>
<van-radio name="2" style="padding:0.1rem 0.05rem">VCD/DVD</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field name="stepper" label="频道值">
<template #input>
<van-stepper v-model="chooseNodeData.default.dspdz" step="2" />
</template>
</van-field>
</div>
</div>
<div class="defBtnDiv">
<van-button round type="info" block>确定</van-button>
</div>
</van-popup>
<!--pupup 新增元素节点-->
<van-popup v-model="popupShow" position="bottom" :style="{ height: '45%' }" >
<!--<van-divider content-position="left"
:style="{ color: '#1989fa', borderColor: '#1989fa' ,padding:'0 0.3rem', margin:'0.2rem'}"
>
选择电器种类
</van-divider>-->
<div class="elecNodes">
<van-col class="elecCol" v-for="(item,index) in elecList" @click="addElc(index)" data-lx="item.type" :key="index" span="6">
<i class="iconfont" v-html="item.icon">{{item.icon}}</i>
<label>{{item.name}}</label>
</van-col>
</div>
</van-popup>
<!--遥控类型弹出层-->
<van-popup v-model="ykTypeShow" :style="{ height: '40%',width:'80%' }" >
<van-picker
title="类型"
show-toolbar
:columns="ykTypesCol"
@confirm="ykTypeConfirm"
/>
</van-popup>
<!--节点验证弹窗-->
<van-popup v-model="verifyPopup" position="bottom" :style="{ height: '35%' }" >
<van-tabs >
<van-tab title="自动验证">
<div class="verifyAuto_div">
<van-button round block :loading="handling==0 ? false : (handling==1?true:false)" :icon="handling==2 ? handlingIcon :''" :loading-text="handling ? '验证中...' : ''" type="info" @click="verifyNodeAuto">{{handlingText}}</van-button>
</div>
</van-tab>
<van-tab title="自定义验证">
<van-form >
<van-field
v-model="customVerify"
name="节点ID"
label="节点ID"
placeholder="节点ID"
:rules="[{ required: false, message: '请填写ID' }]"
/>
<div style="margin: 16px;">
<van-button round block type="info" native-type="submit">
提交
</van-button>
</div>
</van-form>
</van-tab>
</van-tabs>
</van-popup>
</div>
</template>
<script>
var nAdd;
import {Notify, Tab, Tabs} from 'vant';
export default{
data(){
return{
svgViewBox:"0 0 600 345",
curent:0,
iData:{},
gStatus:0,
preClick:"",
clicked:1 //点击状态 用于区分单击以及双击
,time:null //用于记录点击时间间隔
,chooseNode:{} //选中的连线节点 {from:"", to:"" , text:""}
,chooseNodeData:{}//选中的节点数据
,pathArr:[[4,2],[6,3]]
,pathAttrArr:[]
,preClientX:0 //上一次clicentx
,preClientY:0 //上一次clienty
,propX:1 //x轴拖动比例
,propY:1 //y轴拖动比例
,svgView:[]
,alertDia:{} //path点击菜单属性 x y
,isMenuShow:false //菜单是否显示
,menuKey:1
,nowChoose:"" //当前选中的节点元素
,pathText:1 //path文本
,nodeText:""//node文本
,svgStatus:1 //当前状态 0修改状态 1绘制状态 2
,popupShow:false //新增元素节点 popup是否显示
,elecList:[] //电器列表
,targetNodes:[] //拖动时的节点列表 img text circle
,nodeMenuShow:false //节点菜单是否显示
,alertNode:{} //节点菜单属性
,verifyPopup:false //验证菜单是否显示
,handling:0 //当前验证按钮状态 0未操作 1验证中 2验证通过 3验证失败
,handlingText:"点击验证" //当前验证按钮文本
,handlingIcon:"checked"
,customVerify:"" //自定义验证输入框
,websocketPath:"ws://121.40.165.18:8800" //websocket链接
,ykTypeShow:false //遥控类型弹出层
,ykTypesCol:["电视","音响","空调","热水器","窗帘"]
,ykTypeText:""
,defaultShow:false
,hasDefault:["rsqyk","dsyk","yxyk","ktyk","clyk"] //遥控器列表
,sceneOption:[
{ text: '迎宾', value: 0 },
{ text: '遥控', value: 1 },
{ text: '场景', value: 2 },
{ text: '退房', value: 3 }
]
,sceneVal:0
}
},
created(){
this.getNodeList();//获取默认节点数据
this.getData(); //获取svg连线数据
},
watch:{
alertDia(){
++this.menuKey;
},
iData:{
handler(){},
deep:true
}
},
mounted(){
//屏蔽手机端长按出现右键菜单
document.getElementsByTagName("body")[0].addEventListener('contextmenu', function(e){
e.preventDefault();
});
//计算拖动移动比例
let svg = document.getElementById("svgDom");
let windowW = window.document.body.offsetWidth ;
let windowH = window.document.body.offsetHeight;
this.svgViewBox = "0 0 "+windowW +" "+windowH;
this.svgView= this.svgViewBox.split(" ");
this.propX = Number((this.svgView[2] / windowW).toFixed(2));
this.propY = Number((this.svgView[3] / windowH).toFixed(2));
},
methods:{
sceneChange(){ //使用情景切换
console.log(this.sceneVal)
},
iconTranscod(type){ //根据类型获取对应图标
let iconUnicode;
this.elecList.forEach(function(item,index){
if(type == item.type){iconUnicode = item.icon}
})
return iconUnicode;
},
verifyDef(type){ //验证是否显示 缺省值按钮
if(this.hasDefault.indexOf(type)>-1){
return true;
}else{
return false;
}
},
defaultNodePop(){ //缺省值弹窗
this.defaultShow = true;
},
verifyNodeAuto(){ //自动验证
this.handling = 1;
this.init();
setTimeout(() =>{
this.handling = 2;
this.handlingText = '验证成功';
this.handlingIcon = "checked"
},3000)
},
verifyNodePop(){ //验证节点数据
this.verifyPopup = true;
},
delNodeData(){ //节点删除
let thisId = this.chooseNodeData.id;
this.iData.node.forEach((item,index)=>{
if(item.id == thisId){this.$delete(this.iData.node,index)}
})
},
updateNodeData(){ //修改节点文本
this.svgStatus = 2;
this.$nextTick(()=>{
this.$refs.nodeText.focus();
});
console.log(this.chooseNodeData)
this.nodeText = this.chooseNodeData.name;
},
ykTypeConfirm(value){
this.ykTypeText = value;
this.chooseNodeData["name"] = value;
this.ykTypeShow = false;
console.log(this.chooseNodeData)
},
addElc(index){ //新增节点元素 添加模板
//获取svg画布当前中心点
let svgBox = this.svgViewBox.split(" ");
svgBox = svgBox.map(Number);
let cenX = svgBox[0] + svgBox[2] / 2;
let cenY = svgBox[1] + svgBox[3] / 2;
let nowElc = this.elecList[index];
let thisId = this.timestampFun();
this.chooseNodeData ={
"id": thisId,
"name": nowElc.name,
"type": nowElc.type,
"x": cenX,
"y": cenY,
"catalog": nowElc.catalog,
"group":"",
"connection":true,
"onlineStatus": false,
"verificaStatus": false,
"default":{
"ktwd":"",
"ktms":"",
"yxyl":"",
"dsxhy":"",
"dspdz":"",
"clkhzt":"2",
"rsqwd":"34"
}
}
this.iData.node.push(this.chooseNodeData)//向数据源中添加数据
this.popupShow = false;
console.log(this.iData)
},
timestampFun() { //获得时间戳
console.log((new Date()).valueOf())
return (new Date()).valueOf();
},
calPosition(item){ //计算图片 文本 圆心的位置
return {
imgPos:[item.x,item.y],
textPos:[Number(item.x) + 20, Number(item.y) + 45],
cirLine:[Number(item.x) + 50,item.y],
cirVer:[Number(item.x) + 50 , Number(item.y) + 15]
}
},
fullUrl(itemName){ //设定图片路径
return `require(@/assets/img/${itemName}.png)`;
},
getNodeList(){ //获取电器节点列表
this.$axios.get('data/elec.json',{ // 还可以直接把参数拼接在url后边
}).then((res)=>{
this.elecList = res.data;
console.log(this.elecList)
}).catch(function (error) {
console.log(error);
});
},
addNode(){ //新增节点
this.popupShow = true;
},
delPath($event){ //连线删除方法
this.$dialog.confirm({
title: '',
message: '确定删除关联吗',
})
.then(() => {
// on confirm
let index = this.pathAttrArr.findIndex(item =>{
if(item.id == this.nowChoose){
return true;
}
})
this.pathAttrArr.splice(index, 1)
})
.catch(() => {
// on cancel
});
this.isMenuShow = false;
},
upPathText(){ /*线条文本修改事件*/
this.svgStatus = 0;
this.$nextTick(()=>{
this.$refs.pathText.focus();
})
//循环获取
let index = this.pathAttrArr.findIndex(item =>{
if(item.id == this.nowChoose){
this.pathText = item.text;
}
})
console.log(this.svgStatus)
},
choosePath($event){ /*线条点击事件*/
this.isMenuShow = true;
this.$set(this.alertDia,'x',$event.x);//触发更新
this.$set(this.alertDia,'y',$event.y);//触发更新
//保存选中节点
this.nowChoose = $event.target.getAttribute("id");
},
getData(){
this.$axios.get('data/indexNew.json',{ // 还可以直接把参数拼接在url后边
}).then((res)=>{
this.iData = res.data;
let Path = this.iData.relation;
Path.forEach((val,index)=>{
setTimeout(()=>{
this.createPathData(val)
},200)
});
}).catch(function (error) {
console.log(error);
});
},
saveData(){ //保存数据
let saveData = {"node":[],"relation":[]};
let allG = document.getElementsByTagName("g");
saveData["node"] = this.iData.node;
this.pathAttrArr.forEach(item=>{
saveData["relation"].push({"from":item.start,"to":item.end,"val":item.text})
})
console.log(JSON.stringify(saveData))
this.$axios.post('http://127.0.0.1:8080/potology',JSON.stringify(saveData)).then((res)=>{
if(res.status == true){
Notify({ type: 'success', message: '保存成功' });
}else{
Notify({ type: 'danger', message: '保存失败 请重新尝试' });
}
}).catch(function (error) {
Notify({ type: 'danger', message: '保存失败 请重新尝试' });
});
},
dragStartSvg(){
console.log("啦啦啦")
},
dragStart(e){ //触摸屏幕
this.targetNodes = this.getSiblings(e.target); //储存预拖动的元素集合 img text circle
/*判断当前菜单 连线菜单 and 节点菜单*/
if(this.isMenuShow == true){
this.isMenuShow = false;
}
if(this.nodeMenuShow = true){
this.nodeMenuShow = false;
}
if(this.svgStatus == 0){ //判断当前显示状态 0 path文本输入状态
this.pathAttrArr.findIndex(item =>{
if(item.id == this.nowChoose){
item.text = this.pathText;
this.svgStatus = 1; //切换显示状态
}
})
}else if(this.svgStatus == 2){ //判断当前显示状态 2 节点文本输入状态
let idataN = this.iData.node;
idataN.findIndex(item =>{
if(item.id == this.nowChoose){
item.name = this.nodeText;
this.svgStatus = 1; //切换显示状态
}
})
}
//记录手指落点坐标
this.preClientX = e.changedTouches[0].clientX;
this.preClientY = e.changedTouches[0].clientY;
},
dragSvg(e){ //画图拖动
let eTarget = e.changedTouches;
// alert(eTarget.length)
if(eTarget.length == 1){ //单指操作 滑动
//计算拖动偏移差
let devX = Number(eTarget[0].clientX - this.preClientX) / this.propX;
let devY = Number(eTarget[0].clientY - this.preClientY) / this.propY;
this.svgView = this.svgViewBox.split(" ");
this.svgViewBox = `${Number(this.svgView[0]) - devX} ${Number(this.svgView[1]) - devY} ${Number(this.svgView[2])} ${Number(this.svgView[3])}`;
this.preClientX = eTarget[0].clientX;
this.preClientY = eTarget[0].clientY;
}else if(eTarget.length == 2){ //双指操作 缩放
}
},
dragDom(e,id){ //元素节点拖动
console.log(e.target.dataset.type)
if(e.target.dataset.type == "text"){ //如果目标节点时text则返回false
return false;
}
let eTarget = e.changedTouches[0];
//计算拖动偏移差
let devX = Number(eTarget.clientX - this.preClientX) / this.propX;
let devY = Number(eTarget.clientY - this.preClientY) /this.propY;
//获取当前图片节点x和y属性
let attrX = Number(eTarget.target.getAttribute("x"));
let attrY = Number(eTarget.target.getAttribute("y"));
this.targetNodes[0].setAttribute("x",(attrX + devX));
this.targetNodes[0].setAttribute("y",(attrY + devY));
this.targetNodes[1].setAttribute("x",(Number(this.targetNodes[1].getAttribute("x")) + devX));
this.targetNodes[1].setAttribute("y",(Number(this.targetNodes[1].getAttribute("y")) + devY));
this.targetNodes[2].setAttribute("cx",(Number(this.targetNodes[2].getAttribute("cx")) + devX));
this.targetNodes[2].setAttribute("cy",(Number(this.targetNodes[2].getAttribute("cy")) + devY));
this.targetNodes[3].setAttribute("cx",(Number(this.targetNodes[3].getAttribute("cx")) + devX));
this.targetNodes[3].setAttribute("cy",(Number(this.targetNodes[3].getAttribute("cy")) + devY));
this.preClientX = eTarget.clientX;
this.preClientY = eTarget.clientY;
this.getRelevantPath(id);
},
getSiblings(target){ //获取当前目标所有同级元素
let parNode = target.parentNode;
let lisg = parNode.childNodes;
return lisg;
},
addClass($event,index){ //元素点击事件
/*计算菜单弹窗相对位置*/
//let menuW = document.getElementById("nodeMenu").clientWidth
this.$set(this.alertNode,'x',$event.x - 120);//触发更新
this.$set(this.alertNode,'y',$event.y - 90);//触发更新
/*存储当前点击元素*/
this.nowChoose = index.id;
this.chooseNodeData = index;
/*判断当前nodechoose对象状态 第二次选择完成连线*/
if(Object.keys(this.chooseNode) != 0){
this.chooseNode["to"] = index.id;
this.createPathData(this.chooseNode); //绘制连接线
for(let key in this.chooseNode){
delete this.chooseNode[key];
} //数组置空 等待下一次操作
Notify.clear();
}else{
this.nodeMenuShow = true;
}
this.curent = index.id; //存入当前点击的元素id
index == this.preClick;
},
createPathLine(){
Notify({ type: 'primary', message: '请选择想要链接的节点' ,duration:'0'});
this.nodeMenuShow = false;
this.chooseNode["from"] = this.nowChoose;
},
getRelevantPath(id){ //获取相关path
this.pathAttrArr.forEach((val,index) => {
if(val["start"] == id || val["end"] == id){
val["d"] = this.createPathD({"from":val["start"],"to":val["end"]});
val["dir"] = this.judgeDir({"from":val["start"],"to":val["end"]});
}
})
},
createPathData(linkA){ //绘制连接线
this.pathAttrArr.push({id:linkA.from+"_"+linkA.to,d: this.createPathD(linkA), start:linkA.from , end:linkA.to , dir:this.judgeDir(linkA) , text:linkA.val});
},
createPathD(linkA){ //获得连接线 return M
let h = 40;
let startX = Number(document.getElementById(linkA.from).getElementsByTagName("text")[0].getAttribute("x")) + h/2;
let startY = Number(document.getElementById(linkA.from).getElementsByTagName("text")[0].getAttribute("y")) + h/2;
let endX = Number(document.getElementById(linkA.to).getElementsByTagName("text")[0].getAttribute("x")) + h/2;
let endY = Number(document.getElementById(linkA.to).getElementsByTagName("text")[0].getAttribute("y")) + h/2;
//几何临边长度
let limb = Math.abs(endX - startX);
let opposite = Math.abs(endY - startY); //几何对边长度
/*求角度*/
let angel = 180*(Math.atan2(opposite,limb))/Math.PI;
let sOpposite = Number(Math.abs((Math.sin(Math.PI*angel/180) * Math.sqrt(2500)).toFixed(2))); //小临边长度
let sLimb = Number(Math.abs((Math.cos(Math.PI*angel/180) * Math.sqrt(2500)).toFixed(2))); //小对边长度
if(startX > endX){ //初始点大于结束点 x
startX = startX - sLimb;
endX = endX + sLimb;
}else{
startX = startX + sLimb;
endX = endX - sLimb;
}
if(startY > endY){ //初始节点大于结束点 y
startY = startY - sOpposite;
endY = endY + sOpposite;
}else{
startY = startY + sOpposite;
endY = endY - sOpposite;
}
return "M"+startX+" "+startY +","+endX+" "+ endY;
},
judgeDir(linkA){ //判断textpath文本方向 return 0 || 180
let startX = Number(document.getElementById(linkA.from).getElementsByTagName("text")[0].getAttribute("x"));
let endX = Number(document.getElementById(linkA.to).getElementsByTagName("text")[0].getAttribute("x"));
let dir = startX < endX ? 0 : 180;
return dir;
}
,getSinDataById(id){ //根据数据id获取当前节点数据对象
},
/*websocket部分*/
init: function () { //初始化链接
if(typeof(WebSocket) === "undefined"){
alert("您的浏览器不支持socket")
}else{
// 实例化socket
this.socket = new WebSocket(this.websocketPath)
// 监听socket连接
this.socket.onopen = this.open
// 监听socket错误信息
this.socket.onerror = this.error
// 监听socket消息
this.socket.onmessage = this.getMessage
}
},
open: function () {
console.log("socket连接成功")
},
error: function () {
console.log("连接错误")
},
getMessage: function (msg) {
console.log(msg.data)
},
send: function () {
this.socket.send(params)
},
close: function () {
console.log("socket已经关闭")
}
}
}
</script>
<style>
* {
-webkit-touch-callout:none;
-webkit-user-select:none;
-khtml-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
}
/*svgDIv*/
.svgDiv{
height: 100%; width: 100%; position: fixed;
}
#svgDom{background: #f2fbff;}
.contents{height: 100%;width: 100%;}
text{fill: #808080;}
.nameText{font-size: 0.35em; text-anchor: middle; dominant-baseline:text-before-edge}
.iconText{font-size: 1rem !important; dominant-baseline: text-before-edge;text-anchor: inherit;}
.selectG text{fill:#41a7bb }
text{
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select:none;
user-select:none;
color: darkgray;
}
.btn_group{
height: 3rem;
width: calc(100% - 1rem);
padding: 0 0.5rem;
position: absolute;
bottom: 0;
/*background-color: dodgerblue;*/
display: flex;
/*align-items: center;*/
justify-content: center;
}
.btn_group button{
box-shadow: 0 0 6px 0px rgb(193 188 188 / 71%);
}
.save_btn{
font-size: 0.8rem;
color: white;
display: flex;
align-items: center;
}
.save_btn i{font-size: 1.3rem;padding: 0 0.4rem;}
/*节点状态*/
.cir-true{fill: rgb(50, 223, 114);}
.cir-false{ fill: #9e9e9e;}
.lab-true{color: rgb(50, 223, 114);}
.lab-false{color: #9E9E9E;}
/*输入框*/
.input-div{height: auto; width: 100%; position: fixed; bottom: 0; z-index: 999; background-color: orange}
#pathtext{border-bottom: 1px solid #c7c7c7;}
#nodetext{border-bottom: 1px solid #C7C7C7;}
/*电器列表*/
.elecNodes{
font-size: 0.35rem;padding: 0.2rem 0.3rem;
}
.elecCol{border:1px solid #f9f4f4;padding: 0.2rem 0.1rem; display: flex;align-items: center; justify-content: center; flex-direction: column;}
.elecCol i{font-size: 0.7rem; color: #b9b9b9;}
.elecCol label{color: #807d7d; padding-top: 0.2rem;}
.ifShowElc{display: none;}
/*缺省值设置*/
.defaultPop{width: 85%; display: flex; align-items: center;}
.defBtnDiv{padding: 0.2rem;}
/*节点菜单*/
.nodeMenu{ position: fixed; top: 3rem; left: 1rem; min-width: 6.5rem;}
.nodeStatus{display: flex; align-items: center; justify-content: space-around; height: 0.5rem; margin-bottom: 0.1rem; background-color: white;box-shadow: 0 0 4px 1px #e2e2e2;}
.verSta,.lineSta{display: flex; align-items: center;}
.nodeStatus label{font-size: 0.35rem; padding: 0 0.1rem; }
.nodeOper {height: 1rem; width: 100%; background-color: white; box-shadow: 0 0 4px 1px #e2e2e2;}
.nodeOper span{min-width:0.8rem;font-size: 0.35rem; color:#708090; display: block; padding: 0 0.3rem; border-right: 1px solid #9E9E9E;}
.nodeOper span:last-child{border: none;}
.operDiv{padding: 0 0.2rem; height: 100%;display: flex; align-items: center; justify-content: center; }
/*验证按钮*/
.verifyAuto_div{margin-top: 20%; padding: 0 0.5rem;}
.van-tab__pane{text-align: center;}
/*环形菜单*/
.menus{
position: fixed;
}
.circle{
/* 这里一定要绝对定位,这样位置才能铺开来 */
position: absolute;
top: -10px;
left: -10px;
height: 0;
width: 0;
line-height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
color: black;
box-shadow: 0 0 3px rgba(234, 130, 6, 0.51);
opacity: 0;
}
.circle i{
color: #828282;
font-size: 0.5rem;
}
.circle1{
animation: dropdown1 ease 0.3s 1 both;
}
.circle1 i{
transform: rotateZ(45deg);
}
.circle2{
animation: dropdown2 ease 0.3s 1 both;
}
.circle2 i{
transform: rotateZ(90deg);
}
.circle3{
animation: dropdown3 ease 0.3s 1 both;
}
.circle3 i{
transform: rotateZ(135deg);
}
.circle4{
animation: dropdown4 ease 0.3s 1 both;
}
.circle4 i{
transform: rotateZ(150deg);
}
@keyframes dropdown1 {
0% { transform:rotateZ(0deg) translateY(0);}
100% { transform:rotateZ(-45deg) translateY(2rem); height: 1rem; width: 1rem; opacity: 1;}
}
@keyframes dropdown2 {
0% {transform:rotateZ(0deg) translateY(0);}
100% { transform:rotateZ(-90deg) translateY(2rem);height: 1rem; width: 1rem;opacity: 1;}
}
@keyframes dropdown3 {
0% { transform:rotateZ(0deg) translateY(0);}
100% { transform:rotateZ(-135deg) translateY(2rem);height: 1rem; width: 1rem;opacity: 1;}
}
@keyframes dropdown4 {
0% { transform:rotateZ(0deg) translateY(0);}
100% { transform:rotateZ(-150deg) translateY(2rem);height: 2rem; width: 2rem;opacity: 1;}
}
</style>