修改组态化

master
夜笙歌 3 months ago
parent 86e7401aac
commit daf973c416

@ -34,7 +34,7 @@
"diagram-js": "12.3.0",
"didi": "9.0.2",
"echarts": "5.5.0",
"element-plus": "2.7.8",
"element-plus": "2.9.0",
"file-saver": "2.0.5",
"fuse.js": "7.0.0",
"highlight.js": "11.9.0",

@ -197,18 +197,18 @@ const findTier = () => {
params[item.name] = item.test;
});
axios.request({
method: constructionDataForm.value.method,
url: constructionDataForm.value.url,
params: constructionDataForm.value.method === 'get' ? params : '',
data: constructionDataForm.value.method === 'post' ? params : ''
}).then(res => {
// request({
// method: constructionDataForm.value.method,
// url: constructionDataForm.value.url,
// params: constructionDataForm.value.method === 'get' ? params : '',
// data: constructionDataForm.value.method === 'post' ? params : ''
// }).then(res => {
// axios.request({
// method: constructionDataForm.value.method,
// url: constructionDataForm.value.url,
// params: constructionDataForm.value.method === 'get' ? params : '',
// data: constructionDataForm.value.method === 'post' ? params : ''
// }).then(res => {
request({
method: constructionDataForm.value.method,
url: constructionDataForm.value.url,
params: constructionDataForm.value.method === 'get' ? params : '',
data: constructionDataForm.value.method === 'post' ? params : ''
}).then(res => {
let data = res.data;
if (data.data) {
Object.keys(data.data?.[0]).forEach(key => {
@ -223,7 +223,7 @@ const findTier = () => {
constructionDataForm.value.outputData.push({
name: key,
remark: comparisonTable[key],
tier: `data,map%${key}`
tier: `rows,map%${key}`
});
});
} else {
@ -234,7 +234,7 @@ const findTier = () => {
constructionDataForm.value.outputData.push({
name: key,
remark: comparisonTable[key],
tier: `data,${key}`
tier: `${key}`
});
}
}

@ -10,6 +10,46 @@
</template>
折线
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'multiLines')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
多折线
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'curve')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
曲线
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'multiCurves')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
多曲线
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'bar')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
柱状图
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'multiBars')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
多柱状图
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'customBoard')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
@ -49,6 +89,14 @@
</template>
映射
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'text')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
文字
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'inputNode')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
@ -65,51 +113,97 @@
</template>
时间
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'img')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
图片
</el-card>
</el-tab-pane>
</el-tabs>
</div>
<div class="content" @drop="onDrop">
<VueFlow :min-zoom="0.01" ref="flowRef" v-model:nodes="nodes" v-model:edges="edges" fit-view-on-init
default-marker-color="#409EFF"
@connect="logEvent('connect', $event)"
@node-click="logEvent('click', $event)"
@node-contextMenu="logEvent('contextmenu', $event)"
@dragover="onDragOver"
>
<Background :size="1" :gap="20" pattern-color="#BDBDBD" style="background-color: #000" />
<div class="pageSetting">
<span class="pageTitle">{{ pageTitle }}</span>
<div class="btns">
<el-button type="primary" text :icon="Setting" @click="pageSetting"></el-button>
<el-button type="primary" text :icon="Check" @click="save"></el-button>
<el-button type="primary" text :icon="Close" @click="clear"></el-button>
</div>
</div>
<div class="flowArea">
<VueFlow :min-zoom="0.01" ref="flowRef" v-model:nodes="nodes" v-model:edges="edges" fit-view-on-init
default-marker-color="#409EFF"
@connect="logEvent('connect', $event)"
@node-click="logEvent('click', $event)"
@node-contextMenu="logEvent('contextmenu', $event)"
@dragover="onDragOver"
>
<Background :size="1" :gap="20" pattern-color="#BDBDBD" style="background-color: #000" />
<template #node-area="areaNodeProps">
<AreaNode v-bind="areaNodeProps"></AreaNode>
</template>
<template #node-customBoard="customBoardNodeProps">
<CustomBoardNode :inputData=getInputData(customBoardNodeProps.id) v-bind="customBoardNodeProps"
@resize="resize"></CustomBoardNode>
</template>
<template #node-customData="customDataNodeProps">
<CustomDataNode :inputData=getInputData(customDataNodeProps.id) v-bind="customDataNodeProps"
@resize="resize"></CustomDataNode>
</template>
<template #node-line="lineNodeProps">
<LineNode :inputData=getInputData(lineNodeProps.id) v-bind="lineNodeProps"
@resize="resize"></LineNode>
</template>
<template #node-data="dataNodeProps">
<DataNode :inputData=getInputData(dataNodeProps.id) v-bind="dataNodeProps"
@resize="resize"></DataNode>
</template>
<template #node-inputNode="inputNodeProps">
<InputNode :inputData=getInputData(inputNodeProps.id) v-bind="inputNodeProps"
@resize="resize"></InputNode>
</template>
<template #node-time="timeNodeProps">
<TimeNode :inputData=getInputData(timeNodeProps.id) v-bind="timeNodeProps"
@resize="resize"></TimeNode>
</template>
<template #node-map="mapNodeProps">
<MapNode :inputData=getInputData(mapNodeProps.id) v-bind="mapNodeProps"
@resize="resize"></MapNode>
</template>
</VueFlow>
<template #node-area="areaNodeProps">
<AreaNode v-bind="areaNodeProps" :pageData="pageData"></AreaNode>
</template>
<template #node-customBoard="customBoardNodeProps">
<CustomBoardNode :inputData=getInputData(customBoardNodeProps.id) v-bind="customBoardNodeProps"
@resize="resize"></CustomBoardNode>
</template>
<template #node-customData="customDataNodeProps">
<CustomDataNode :inputData=getInputData(customDataNodeProps.id) v-bind="customDataNodeProps"
@resize="resize"></CustomDataNode>
</template>
<template #node-line="lineNodeProps">
<LineNode :inputData=getInputData(lineNodeProps.id) v-bind="lineNodeProps"
@resize="resize"></LineNode>
</template>
<template #node-multiLines="multiLinesNodeProps">
<MultiLinesNode :inputData=getInputData(multiLinesNodeProps.id) v-bind="multiLinesNodeProps"
@resize="resize"></MultiLinesNode>
</template>
<template #node-curve="curveNodeProps">
<CurveNode :inputData=getInputData(curveNodeProps.id) v-bind="curveNodeProps"
@resize="resize"></CurveNode>
</template>
<template #node-multiCurves="multiCurvesNodeProps">
<MultiCurvesNode :inputData=getInputData(multiCurvesNodeProps.id) v-bind="multiCurvesNodeProps"
@resize="resize"></MultiCurvesNode>
</template>
<template #node-bar="barNodeProps">
<BarNode :inputData=getInputData(barNodeProps.id) v-bind="barNodeProps"
@resize="resize"></BarNode>
</template>
<template #node-multiBars="multiBarsNodeProps">
<MultiBarsNode :inputData=getInputData(multiBarsNodeProps.id) v-bind="multiBarsNodeProps"
@resize="resize"></MultiBarsNode>
</template>
<template #node-data="dataNodeProps">
<DataNode :inputData=getInputData(dataNodeProps.id) v-bind="dataNodeProps"
@resize="resize"></DataNode>
</template>
<template #node-text="textNodeProps">
<TextNode :inputData=getInputData(textNodeProps.id) v-bind="textNodeProps"
@resize="resize"></TextNode>
</template>
<template #node-img="imgNodeProps">
<ImgNode :inputData=getInputData(imgNodeProps.id) v-bind="imgNodeProps"
@resize="resize"></ImgNode>
</template>
<template #node-inputNode="inputNodeProps">
<InputNode :inputData=getInputData(inputNodeProps.id) v-bind="inputNodeProps"
@resize="resize"></InputNode>
</template>
<template #node-time="timeNodeProps">
<TimeNode :inputData=getInputData(timeNodeProps.id) v-bind="timeNodeProps"
@resize="resize"></TimeNode>
</template>
<template #node-map="mapNodeProps">
<MapNode :inputData=getInputData(mapNodeProps.id) v-bind="mapNodeProps"
@resize="resize"></MapNode>
</template>
</VueFlow>
</div>
</div>
<div class="rightPanel">
<el-form :model="nodeAttrForm" label-width="auto" style="max-width: 600px" v-if="nodeAttrForm">
@ -146,26 +240,100 @@
添加映射
</el-button>
</el-form-item>
<el-form-item label="内容" v-if="Object.keys(nodeAttrForm).includes('text')">
<el-input v-model="nodeAttrForm.text" style="width: 100%" />
</el-form-item>
<el-form-item label="对齐方式" v-if="Object.keys(nodeAttrForm).includes('align')">
<el-select
v-model="nodeAttrForm.align"
placeholder="Select"
style="width: 100%"
>
<el-option
label="左对齐"
value="left"
/>
<el-option
label="居中对齐"
value="center"
/>
<el-option
label="右对齐"
value="right"
/>
</el-select>
</el-form-item>
<el-form-item label="文字颜色" v-if="Object.keys(nodeAttrForm).includes('color')">
<el-color-picker v-model="nodeAttrForm.color" show-alpha />
</el-form-item>
<el-form-item label="图片路径" v-if="Object.keys(nodeAttrForm).includes('imgSrc')">
<el-input v-model="nodeAttrForm.imgSrc" style="width: 100%" />
</el-form-item>
<el-form-item label="标题" v-if="Object.keys(nodeAttrForm).includes('title')">
<el-input v-model="nodeAttrForm.title" style="width: 100%" />
</el-form-item>
<el-form-item label="数据名称" v-if="Object.keys(nodeAttrForm).includes('yNames')">
<el-input-tag v-model="nodeAttrForm.yNames" placeholder="回车确认" />
</el-form-item>
</el-form>
{{ nodes }}
<br />
---
<br />
{{ edges }}
</div>
<el-dialog v-model="pageSettingVisible" title="页面设置" width="500">
<el-form :model="pageSettingForm">
<el-form-item label="页面大小" label-width="80px">
<el-input v-model="pageSettingForm.width" style="width: calc(50% - 10px)" autocomplete="off"
placeholder="缺省则为不限制" />
<span style="width: 20px;text-align: center">
X
</span>
<el-input v-model="pageSettingForm.height" style="width: calc(50% - 10px)" autocomplete="off"
placeholder="缺省则为不限制" />
</el-form-item>
<el-form-item label="页面背景" label-width="80px">
<el-upload
class="avatar-uploader"
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
:show-file-list="false"
:limit="1"
accept=".jpg,.png"
:before-upload="pageBgUploadSuccess"
>
<img v-if="pageSettingForm.bg" :src="pageSettingForm.bg" class="avatar" />
<el-icon v-else class="avatar-uploader-icon">
<Plus />
</el-icon>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="pageSettingVisible = false">取消</el-button>
<el-button type="primary" @click="setPageData">
确定
</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { v4 as uuidv4 } from 'uuid';
import { Setting, Check, Close, Plus } from '@element-plus/icons-vue';
import { MarkerType, VueFlow, useVueFlow } from '@vue-flow/core';
import { Background } from '@vue-flow/background';
import { StarFilled } from '@element-plus/icons-vue';
import LineNode from './nodes/board/lineNode.vue';
import MultiLinesNode from './nodes/board/multiLinesNode.vue';
import CurveNode from './nodes/board/curveNode.vue';
import MultiCurvesNode from './nodes/board/multiCurvesNode.vue';
import BarNode from './nodes/board/barNode.vue';
import MultiBarsNode from './nodes/board/multiBarsNode.vue';
import CustomBoardNode from './nodes/board/customBoardNode.vue';
import DataNode from './nodes/data/dataNode.vue';
import CustomDataNode from './nodes/data/customDataNode.vue';
import MapNode from './nodes/form/mapNode.vue';
import InputNode from './nodes/form/inputNode.vue';
import TimeNode from './nodes/form/timeNode.vue';
import TextNode from './nodes/form/textNode.vue';
import ImgNode from './nodes/form/imgNode.vue';
import AreaNode from './nodes/other/areaNode.vue';
import tool from './tool';
@ -178,520 +346,29 @@ const {
addEdges
} = useVueFlow();
const pageSettingVisible = ref(false);
const pageSettingForm = ref({});
const pageTitle = ref('页面名称');
const leftPanelState = ref('1');
const areaId = `area_${uuidv4().replaceAll('-', '_')}`;
// const nodes = ref([{
// id: areaId,
// name: 'area',
// type: 'area',
// position: {
// x: 0,
// y: 0
// },
// data: { label: areaId }
// }]);
// const edges = ref([]);
const nodes = ref([
{
'id': 'area_474cfc79_4011_495e_9fb3_5e268ad09cfe',
'type': 'area',
'dimensions': { 'width': 1910, 'height': 970 },
'computedPosition': { 'x': 0, 'y': 0, 'z': 0 },
'handleBounds': { 'source': null, 'target': null },
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 0, 'y': 0 },
'data': { 'label': 'area_474cfc79_4011_495e_9fb3_5e268ad09cfe' },
'events': {},
'name': 'area'
}, {
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'type': 'time',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 30.66721629102227, 'y': 21.424672395499982, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-s',
'type': 'source',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-t',
'type': 'target',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 30.66721629102227, 'y': 21.424672395499982 },
'data': {
'label': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'options': { 'startTimeId': 'startTime', 'endTimeId': 'endTime' },
'outputData': {}
},
'events': {},
'name': 'time'
}, {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'customData',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 202.86901500385937, 'y': -165.53728049272314, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'type': 'source',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-t',
'type': 'target',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 202.86901500385937, 'y': -165.53728049272314 },
'data': {
'label': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'options': {},
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
},
'customData': {
'inputData': [{ 'name': 'startTime' }, { 'name': 'endTime' }],
'outputData': [{ 'name': 'time', 'remark': '时间', 'tier': 'data,map%time' }, {
'name': 'value',
'remark': '值',
'tier': 'data,map%value'
}],
'name': '测试2',
'url': 'http://localhost:3000/test/getDevice',
'method': 'post'
}
},
'events': {},
'name': 'customData'
}, {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'map',
'dimensions': { 'width': 100, 'height': 30 },
'computedPosition': { 'x': 384.9109165002872, 'y': -88.45647535459605, 'z': 1000 },
'handleBounds': {
'source': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'type': 'source',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'right',
'x': 97,
'y': 12,
'width': 6,
'height': 6
}],
'target': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-t',
'type': 'target',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'left',
'x': -3,
'y': 12,
'width': 6,
'height': 6
}]
},
'selected': true,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 384.9109165002872, 'y': -88.45647535459605 },
'data': {
'label': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'options': { 'dataMap': [{ 'source': 'value', 'target': 'y1' }, { 'source': 'time', 'target': 'x1' }] },
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
}
},
'events': {},
'name': 'map'
}, {
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'type': 'line',
'dimensions': { 'width': 300, 'height': 150 },
'computedPosition': { 'x': 144.31114508758668, 'y': 202.1298087493107, 'z': 0 },
'handleBounds': {
'source': null,
'target': [{
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772.-t',
'type': 'target',
'nodeId': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'position': 'left',
'x': -3,
'y': 71.99951171875,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 144.31114508758668, 'y': 202.1298087493107 },
'data': { 'label': 'line_650b8add_cbce_402a_a8e5_2c5769526772', 'options': {}, 'outputData': {} },
'events': {},
'name': 'line'
}]);
const edges = ref([
{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b---customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'bezier',
'source': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'target': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'sourceHandle': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-s',
'data': {},
'events': {},
'label': '',
'animated': true,
'markerEnd': 'arrowclosed',
'style': { 'stroke': '#409EFF' },
'sourceNode': {
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'type': 'time',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 30.66721629102227, 'y': 21.424672395499982, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-s',
'type': 'source',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-t',
'type': 'target',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 30.66721629102227, 'y': 21.424672395499982 },
'data': {
'label': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'options': { 'startTimeId': 'startTime', 'endTimeId': 'endTime' },
'outputData': {}
},
'events': {},
'name': 'time'
},
'targetNode': {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'customData',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 202.86901500385937, 'y': -165.53728049272314, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'type': 'source',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-t',
'type': 'target',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 202.86901500385937, 'y': -165.53728049272314 },
'data': {
'label': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'options': {},
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
},
'customData': {
'inputData': [{ 'name': 'startTime' }, { 'name': 'endTime' }],
'outputData': [{ 'name': 'time', 'remark': '时间', 'tier': 'data,map%time' }, {
'name': 'value',
'remark': '值',
'tier': 'data,map%value'
}],
'name': '测试2',
'url': 'http://localhost:3000/test/getDevice',
'method': 'post'
}
},
'events': {},
'name': 'customData'
},
'sourceX': 133.66721629102227,
'sourceY': 71.42467239549998,
'targetX': 199.86901500385937,
'targetY': -115.53728049272314
}, {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699---map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'bezier',
'source': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'target': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'sourceHandle': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'data': {},
'events': {},
'label': '',
'animated': true,
'markerEnd': 'arrowclosed',
'style': { 'stroke': '#409EFF' },
'sourceNode': {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'customData',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 202.86901500385937, 'y': -165.53728049272314, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'type': 'source',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-t',
'type': 'target',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 202.86901500385937, 'y': -165.53728049272314 },
'data': {
'label': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'options': {},
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
},
'customData': {
'inputData': [{ 'name': 'startTime' }, { 'name': 'endTime' }],
'outputData': [{ 'name': 'time', 'remark': '时间', 'tier': 'data,map%time' }, {
'name': 'value',
'remark': '值',
'tier': 'data,map%value'
}],
'name': '测试2',
'url': 'http://localhost:3000/test/getDevice',
'method': 'post'
}
},
'events': {},
'name': 'customData'
},
'targetNode': {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'map',
'dimensions': { 'width': 100, 'height': 30 },
'computedPosition': { 'x': 384.9109165002872, 'y': -88.45647535459605, 'z': 1000 },
'handleBounds': {
'source': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'type': 'source',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'right',
'x': 97,
'y': 12,
'width': 6,
'height': 6
}],
'target': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-t',
'type': 'target',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'left',
'x': -3,
'y': 12,
'width': 6,
'height': 6
}]
},
'selected': true,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 384.9109165002872, 'y': -88.45647535459605 },
'data': {
'label': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'options': { 'dataMap': [{ 'source': 'value', 'target': 'y1' }, { 'source': 'time', 'target': 'x1' }] },
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
}
},
'events': {},
'name': 'map'
},
'sourceX': 305.8690150038594,
'sourceY': -115.53728049272314,
'targetX': 381.9109165002872,
'targetY': -73.45647535459605
}, {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3---line_650b8add_cbce_402a_a8e5_2c5769526772',
'type': 'bezier',
'source': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'target': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'sourceHandle': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'data': {},
'events': {},
'label': '',
'animated': true,
'markerEnd': 'arrowclosed',
'style': { 'stroke': '#409EFF' },
'sourceNode': {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'map',
'dimensions': { 'width': 100, 'height': 30 },
'computedPosition': { 'x': 384.9109165002872, 'y': -88.45647535459605, 'z': 1000 },
'handleBounds': {
'source': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'type': 'source',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'right',
'x': 97,
'y': 12,
'width': 6,
'height': 6
}],
'target': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-t',
'type': 'target',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'left',
'x': -3,
'y': 12,
'width': 6,
'height': 6
}]
},
'selected': true,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 384.9109165002872, 'y': -88.45647535459605 },
'data': {
'label': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'options': { 'dataMap': [{ 'source': 'value', 'target': 'y1' }, { 'source': 'time', 'target': 'x1' }] },
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
}
},
'events': {},
'name': 'map'
},
'targetNode': {
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'type': 'line',
'dimensions': { 'width': 300, 'height': 150 },
'computedPosition': { 'x': 144.31114508758668, 'y': 202.1298087493107, 'z': 0 },
'handleBounds': {
'source': null,
'target': [{
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772.-t',
'type': 'target',
'nodeId': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'position': 'left',
'x': -3,
'y': 71.99951171875,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 144.31114508758668, 'y': 202.1298087493107 },
'data': { 'label': 'line_650b8add_cbce_402a_a8e5_2c5769526772', 'options': {}, 'outputData': {} },
'events': {},
'name': 'line'
},
'sourceX': 487.9109165002872,
'sourceY': -73.45647535459605,
'targetX': 141.31114508758668,
'targetY': 277.1293204680607
}]);
const pageData = ref({});
const nodes = ref([{
id: `area_${uuidv4().replaceAll('-', '_')}`,
name: 'area',
type: 'area',
position: {
x: 0,
y: 0
},
data: {}
}]);
const edges = ref([]);
const customData = ref([]);
onMounted(() => {
onMounted(async () => {
customData.value = JSON.parse(localStorage.getItem('DATANODE') || '[]');
await nextTick();
nodes.value = reactive(JSON.parse(localStorage.getItem('NODES') || '[]'));
edges.value = JSON.parse(localStorage.getItem('EDGES') || '[]');
pageData.value = JSON.parse(localStorage.getItem('PAGEDATA'));
});
const nodeAttrForm = ref({});
const logEvent = async (eventname, event) => {
@ -746,10 +423,76 @@ const getInputData = (e) => {
});
return outputData;
};
const pageSetting = () => {
pageSettingVisible.value = true;
pageSettingForm.value = JSON.parse(localStorage.getItem('PAGEDATA'));
};
const save = () => {
console.log(nodes.value);
localStorage.setItem('NODES', JSON.stringify(nodes.value.map(e => {
let data = {};
let savaField = ['customData', 'options'];
Object.keys(e.data).forEach((key) => {
if (savaField.includes(key)) {
data[key] = e.data[key];
} else {
if (Array.isArray(e.data[key])) {
data[key] = [];
} else if (e.data[key] && typeof e.data[key] === 'object' && !Array.isArray(e.data[key])) {
data[key] = {};
} else {
data[key] = null;
}
}
});
return {
id: e.id,
name: e.name,
dimensions: e.dimensions,
position: e.position,
type: e.type,
data: data
};
})));
localStorage.setItem('EDGES', JSON.stringify(edges.value));
};
const clear = () => {
nodes.value = [{
id: `area_${uuidv4().replaceAll('-', '_')}`,
name: 'area',
type: 'area',
position: {
x: 0,
y: 0
},
data: {}
}];
edges.value = [];
};
const pageBgUploadSuccess = (file) => {
const getFileText = (file) => {
const reader = new FileReader();
reader.onload = function(e) {
const fileContent = e.target.result;
console.log(fileContent);
pageSettingForm.value.bg = fileContent;
};
reader.readAsDataURL(file);
};
getFileText(file);
return false;
};
const setPageData = () => {
pageData.value = JSON.parse(JSON.stringify(pageSettingForm.value));
localStorage.setItem('PAGEDATA', JSON.stringify(pageData.value));
pageSettingVisible.value = false;
};
</script>
<style lang="less" scoped>
:deep(.vue-flow__node-area) {
z-index: 0 !important;
z-index: -1 !important;
pointer-events: none !important;
}
@ -760,14 +503,44 @@ const getInputData = (e) => {
width: 300px;
height: 100%;
border-right: 1px solid #409EFF;
overflow: auto;
}
.content {
position: absolute;
top: 0;
left: 320px;
width: calc(100% - 320px - 320px);
left: 300px;
width: calc(100% - 300px - 300px);
height: 100%;
.pageSetting {
width: 100%;
height: 50px;
border-bottom: 1px solid #409EFF;
position: relative;
.pageTitle {
line-height: 50px;
margin-left: 8px;
}
.btns {
position: absolute;
right: 8px;
display: inline-block;
line-height: 50px;
button {
vertical-align: inherit;
}
}
}
.flowArea {
width: 100%;
height: calc(100% - 50px);
}
}
.rightPanel {
@ -777,5 +550,27 @@ const getInputData = (e) => {
width: 300px;
height: 100%;
border-left: 1px solid #409EFF;
overflow: auto;
}
:deep(.avatar-uploader) .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
:deep(.avatar-uploader) .el-upload:hover {
border-color: var(--el-color-primary);
}
:deep(.el-icon.avatar-uploader-icon) {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
</style>

@ -0,0 +1,224 @@
<template>
<div
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<NodeResizer color="#000" v-if="!props.isView && !props.isHideHandle && props.selected" @resize="resize" />
<div class="custom-node"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<div style="width: 100%;height: 100%" ref="chartRef" />
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
import * as echarts from 'echarts';
const props = defineProps({
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const chartRef = ref();
let chart = null;
const colorList = ['#9E87FF', '#73DDFF', '#fe9a8b', '#F56948', '#9E87FF'];
const getOption = () => {
const chartOption = {
title: {
text: props.data.options.title || '设备运行数量',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
left: '0',
top: '5%'
},
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
label: {
show: true,
backgroundColor: '#fff',
color: '#000',
borderColor: 'rgba(0,0,0,0)',
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetY: 0
},
lineStyle: {
width: 0
}
},
backgroundColor: '#fff',
textStyle: {
color: '#000'
},
padding: [10, 10],
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)'
},
grid: {
top: '30%',
bottom: '10%'
},
xAxis: [
{
type: 'category',
axisLine: {
lineStyle: {
color: '#DCE2E8'
}
},
axisTick: {
show: true
},
axisLabel: {
interval: 0,
textStyle: {
color: '#fff'
},
// x
fontSize: 12,
// margin:x
margin: 3
},
axisPointer: {
label: {
padding: [0, 0, 0, 0],
margin: 0,
//
fontSize: 12
}
},
boundaryGap: true
}
],
yAxis: [
{
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#fff'
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
},
splitLine: {
show: false
}
}
],
series: [
{
type: 'bar',
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
offset: 0,
color: '#0372FF'
},
{
offset: 1,
color: '#75ECFF'
}
])
}
},
barMaxWidth: 50,
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 16
}
}
]
};
let xData = [props.inputData?.x1 || []];
let yData = [props.inputData?.y1 || []];
let length = Math.min(...xData.map(e => e.length), ...yData.map(e => e.length));
let source = [['product', ...[props.data.options?.yNames?.[0] || '数量']]];
Array(length).fill(0).forEach((_, i) => {
let item = [];
xData.forEach(e => {
item.push(e[i]);
});
yData.forEach(e => {
item.push(e[i]);
});
source.push(item);
});
return {
...chartOption,
dataset: {
source
}
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg' // 使 SVG
});
chart.setOption(getOption(), true);
});
watch(() => [JSON.parse(JSON.stringify(props.inputData)), JSON.parse(JSON.stringify(props.data.options))], (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']);
const resize = (e) => {
chart.resize();
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
width: 100%;
height: 100%;
}
</style>

@ -0,0 +1,231 @@
<template>
<div
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<NodeResizer color="#000" v-if="!props.isView && !props.isHideHandle && props.selected" @resize="resize" />
<div class="custom-node"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<div style="width: 100%;height: 100%" ref="chartRef" />
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
import * as echarts from 'echarts';
const props = defineProps({
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const chartRef = ref();
let chart = null;
const colorList = ['#9E87FF', '#73DDFF', '#fe9a8b', '#F56948', '#9E87FF'];
const getOption = () => {
const chartOption = {
title: {
text: props.data.options.title || '设备运行数量',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
left: '0',
top: '5%'
},
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
label: {
show: true,
backgroundColor: '#fff',
color: '#000',
borderColor: 'rgba(0,0,0,0)',
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetY: 0
},
lineStyle: {
width: 0
}
},
backgroundColor: '#fff',
textStyle: {
color: '#000'
},
padding: [10, 10],
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)'
},
grid: {
top: '30%',
bottom: '10%'
},
xAxis: [
{
type: 'category',
axisLine: {
lineStyle: {
color: '#DCE2E8'
}
},
axisTick: {
show: true
},
axisLabel: {
interval: 0,
textStyle: {
color: '#fff'
},
// x
fontSize: 12,
// margin:x
margin: 3
},
axisPointer: {
label: {
padding: [0, 0, 0, 0],
margin: 0,
//
fontSize: 12
}
},
boundaryGap: true
}
],
yAxis: [
{
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#fff'
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
},
splitLine: {
show: false
}
}
],
series: [
{
type: 'line',
symbolSize: 1,
symbol: 'circle',
smooth: true,
yAxisIndex: 0,
showSymbol: false,
lineStyle: {
width: 1,
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: '#9effff'
},
{
offset: 1,
color: '#9E87FF'
}
]),
shadowColor: 'rgba(158,135,255, 0.3)',
shadowBlur: 10,
shadowOffsetY: 20
},
itemStyle: {
normal: {
color: colorList[0],
borderColor: colorList[0]
}
}
}
]
};
let xData = [props.inputData?.x1 || []];
let yData = [props.inputData?.y1 || []];
let length = Math.min(...xData.map(e => e.length), ...yData.map(e => e.length));
let source = [['product', ...[props.data.options?.yNames?.[0] || '数量']]];
Array(length).fill(0).forEach((_, i) => {
let item = [];
xData.forEach(e => {
item.push(e[i]);
});
yData.forEach(e => {
item.push(e[i]);
});
source.push(item);
});
return {
...chartOption,
dataset: {
source
}
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg' // 使 SVG
});
chart.setOption(getOption(), true);
});
watch(() => [JSON.parse(JSON.stringify(props.inputData)), JSON.parse(JSON.stringify(props.data.options))], (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']);
const resize = (e) => {
chart.resize();
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
width: 100%;
height: 100%;
}
</style>

@ -220,7 +220,7 @@ onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons');
chart.setOption(getOption(), true);
});
watch(() => props.inputData, () => {
watch(() => JSON.parse(JSON.stringify(props.inputData)), () => {
chart.setOption(getOption(), true);
}, { deep: true });
</script>

@ -49,145 +49,145 @@ const props = defineProps({
const chartRef = ref();
let chart = null;
const colorList = ['#9E87FF', '#73DDFF', '#fe9a8b', '#F56948', '#9E87FF'];
const chartOption = {
title: {
text: '设备运行数量',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
left: '0',
top: '5%'
},
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
label: {
show: true,
backgroundColor: '#fff',
color: '#000',
borderColor: 'rgba(0,0,0,0)',
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetY: 0
const getOption = () => {
const chartOption = {
title: {
text: props.data.options.title || '设备运行数量',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
lineStyle: {
width: 0
left: '0',
top: '5%'
},
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
backgroundColor: '#fff',
textStyle: {
color: '#000'
},
padding: [10, 10],
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)'
},
grid: {
top: '30%',
bottom: '10%'
},
xAxis: [
{
type: 'category',
axisLine: {
lineStyle: {
color: '#DCE2E8'
}
},
axisTick: {
show: true
},
axisLabel: {
interval: 0,
textStyle: {
color: '#fff'
},
// x
fontSize: 12,
// margin:x
margin: 3
},
tooltip: {
trigger: 'axis',
axisPointer: {
label: {
padding: [0, 0, 0, 0],
margin: 0,
//
fontSize: 12
}
},
boundaryGap: true
}
],
yAxis: [
{
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
show: true,
backgroundColor: '#fff',
color: '#000',
borderColor: 'rgba(0,0,0,0)',
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetY: 0
},
lineStyle: {
color: '#fff'
width: 0
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
backgroundColor: '#fff',
textStyle: {
color: '#000'
},
splitLine: {
show: false
}
}
],
series: [
{
type: 'line',
symbolSize: 1,
symbol: 'circle',
smooth: true,
yAxisIndex: 0,
showSymbol: false,
lineStyle: {
width: 1,
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: '#9effff'
},
{
offset: 1,
color: '#9E87FF'
padding: [10, 10],
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)'
},
grid: {
top: '30%',
bottom: '10%'
},
xAxis: [
{
type: 'category',
axisLine: {
lineStyle: {
color: '#DCE2E8'
}
]),
shadowColor: 'rgba(158,135,255, 0.3)',
shadowBlur: 10,
shadowOffsetY: 20
},
itemStyle: {
normal: {
color: colorList[0],
borderColor: colorList[0]
},
axisTick: {
show: true
},
axisLabel: {
interval: 0,
textStyle: {
color: '#fff'
},
// x
fontSize: 12,
// margin:x
margin: 3
},
axisPointer: {
label: {
padding: [0, 0, 0, 0],
margin: 0,
//
fontSize: 12
}
},
boundaryGap: true
}
],
yAxis: [
{
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#fff'
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
},
splitLine: {
show: false
}
}
}
]
};
const getOption = () => {
],
series: [
{
type: 'line',
symbolSize: 1,
symbol: 'circle',
smooth: false,
yAxisIndex: 0,
showSymbol: false,
lineStyle: {
width: 1,
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: '#9effff'
},
{
offset: 1,
color: '#9E87FF'
}
]),
shadowColor: 'rgba(158,135,255, 0.3)',
shadowBlur: 10,
shadowOffsetY: 20
},
itemStyle: {
normal: {
color: colorList[0],
borderColor: colorList[0]
}
}
}
]
};
let xData = [props.inputData?.x1 || []];
let yData = [props.inputData?.y1 || []];
let length = Math.min(...xData.map(e => e.length), ...yData.map(e => e.length));
let source = [['product', '数量']];
let source = [['product', ...[props.data.options?.yNames?.[0] || '数量']]];
Array(length).fill(0).forEach((_, i) => {
let item = [];
xData.forEach(e => {
@ -199,18 +199,23 @@ const getOption = () => {
source.push(item);
});
return {
...chartOption,
dataset: {
source
}
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons');
chart.setOption(chartOption, true);
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg' // 使 SVG
});
chart.setOption(getOption(), true);
});
watch(() => props.inputData, () => {
chart && chart.setOption(getOption(), false);
}, { deep: true });
watch(() => [JSON.parse(JSON.stringify(props.inputData)), JSON.parse(JSON.stringify(props.data.options))], (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']);
const resize = (e) => {
chart.resize();

@ -0,0 +1,234 @@
<template>
<div
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<NodeResizer color="#000" v-if="!props.isView && !props.isHideHandle && props.selected" @resize="resize" />
<div class="custom-node"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<div style="width: 100%;height: 100%" ref="chartRef" />
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
import * as echarts from 'echarts';
const props = defineProps({
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const chartRef = ref();
let chart = null;
const colorList = ['#9E87FF', '#73DDFF', '#fe9a8b', '#F56948', '#9E87FF'];
const sortData = (data) => {
let obj = [];
Object.keys(data).forEach((key) => {
if (key.includes('y') && !isNaN(key.split('y')?.[1])) {
obj.push([parseFloat(key.split('y')?.[1]), data[key]]);
}
});
obj.sort((a, b) => a[0] - b[0]);
return obj.map(e => e[1]) || [];
};
const getOption = () => {
let xData = [props.inputData?.x1 || []];
let yData = sortData(props.inputData);
let length = Math.min(...xData.map(e => e.length), ...yData.map(e => e.length));
let source = [['product', ...props.data.options?.yNames || []]];
const chartOption = {
title: {
text: props.data.options.title || '设备运行数量',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
left: '0',
top: '5%'
},
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
label: {
show: true,
backgroundColor: '#fff',
color: '#000',
borderColor: 'rgba(0,0,0,0)',
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetY: 0
},
lineStyle: {
width: 0
}
},
backgroundColor: '#fff',
textStyle: {
color: '#000'
},
padding: [10, 10],
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)'
},
grid: {
top: '30%',
bottom: '10%'
},
xAxis: [
{
type: 'category',
axisLine: {
lineStyle: {
color: '#DCE2E8'
}
},
axisTick: {
show: true
},
axisLabel: {
interval: 0,
textStyle: {
color: '#fff'
},
// x
fontSize: 12,
// margin:x
margin: 3
},
axisPointer: {
label: {
padding: [0, 0, 0, 0],
margin: 0,
//
fontSize: 12
}
},
boundaryGap: true
}
],
yAxis: {
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#fff'
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
},
splitLine: {
show: false
}
},
series: (props.data.options?.yNames || []).map(() => {
return {
type: 'bar',
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
offset: 0,
color: '#0372FF'
},
{
offset: 1,
color: '#75ECFF'
}
])
}
},
barMaxWidth: 50,
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 16
}
};
})
};
Array(length).fill(0).forEach((_, i) => {
let item = [];
xData.forEach(e => {
item.push(e[i]);
});
yData.forEach(e => {
item.push(e[i]);
});
source.push(item);
});
return {
...chartOption,
dataset: {
source
}
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg' // 使 SVG
});
chart && chart.setOption(getOption(), true);
});
watch(() => [JSON.parse(JSON.stringify(props.inputData)), JSON.parse(JSON.stringify(props.data.options))], (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']);
const resize = (e) => {
chart.resize();
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
width: 100%;
height: 100%;
}
</style>

@ -0,0 +1,241 @@
<template>
<div
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<NodeResizer color="#000" v-if="!props.isView && !props.isHideHandle && props.selected" @resize="resize" />
<div class="custom-node"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<div style="width: 100%;height: 100%" ref="chartRef" />
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
import * as echarts from 'echarts';
const props = defineProps({
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const chartRef = ref();
let chart = null;
const colorList = ['#9E87FF', '#73DDFF', '#fe9a8b', '#F56948', '#9E87FF'];
const sortData = (data) => {
let obj = [];
Object.keys(data).forEach((key) => {
if (key.includes('y') && !isNaN(key.split('y')?.[1])) {
obj.push([parseFloat(key.split('y')?.[1]), data[key]]);
}
});
obj.sort((a, b) => a[0] - b[0]);
return obj.map(e => e[1]) || [];
};
const getOption = () => {
let xData = [props.inputData?.x1 || []];
let yData = sortData(props.inputData);
let length = Math.min(...xData.map(e => e.length), ...yData.map(e => e.length));
let source = [['product', ...props.data.options?.yNames]];
const chartOption = {
title: {
text: props.data.options.title || '设备运行数量',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
left: '0',
top: '5%'
},
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
label: {
show: true,
backgroundColor: '#fff',
color: '#000',
borderColor: 'rgba(0,0,0,0)',
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetY: 0
},
lineStyle: {
width: 0
}
},
backgroundColor: '#fff',
textStyle: {
color: '#000'
},
padding: [10, 10],
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)'
},
grid: {
top: '30%',
bottom: '10%'
},
xAxis: [
{
type: 'category',
axisLine: {
lineStyle: {
color: '#DCE2E8'
}
},
axisTick: {
show: true
},
axisLabel: {
interval: 0,
textStyle: {
color: '#fff'
},
// x
fontSize: 12,
// margin:x
margin: 3
},
axisPointer: {
label: {
padding: [0, 0, 0, 0],
margin: 0,
//
fontSize: 12
}
},
boundaryGap: true
}
],
yAxis: {
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#fff'
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
},
splitLine: {
show: false
}
},
series: props.data.options?.yNames.map(() => {
return {
type: 'line',
symbolSize: 1,
symbol: 'circle',
smooth: true,
yAxisIndex: 0,
showSymbol: false,
lineStyle: {
width: 1,
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: '#9effff'
},
{
offset: 1,
color: '#9E87FF'
}
]),
shadowColor: 'rgba(158,135,255, 0.3)',
shadowBlur: 10,
shadowOffsetY: 20
},
itemStyle: {
normal: {
color: colorList[0],
borderColor: colorList[0]
}
}
};
})
};
Array(length).fill(0).forEach((_, i) => {
let item = [];
xData.forEach(e => {
item.push(e[i]);
});
yData.forEach(e => {
item.push(e[i]);
});
source.push(item);
});
return {
...chartOption,
dataset: {
source
}
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg' // 使 SVG
});
chart && chart.setOption(getOption(), true);
});
watch(() => [JSON.parse(JSON.stringify(props.inputData)), JSON.parse(JSON.stringify(props.data.options))], (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']);
const resize = (e) => {
chart.resize();
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
width: 100%;
height: 100%;
}
</style>

@ -0,0 +1,241 @@
<template>
<div
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<NodeResizer color="#000" v-if="!props.isView && !props.isHideHandle && props.selected" @resize="resize" />
<div class="custom-node"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<div style="width: 100%;height: 100%" ref="chartRef" />
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
import * as echarts from 'echarts';
const props = defineProps({
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const chartRef = ref();
let chart = null;
const colorList = ['#9E87FF', '#73DDFF', '#fe9a8b', '#F56948', '#9E87FF'];
const sortData = (data) => {
let obj = [];
Object.keys(data).forEach((key) => {
if (key.includes('y') && !isNaN(key.split('y')?.[1])) {
obj.push([parseFloat(key.split('y')?.[1]), data[key]]);
}
});
obj.sort((a, b) => a[0] - b[0]);
return obj.map(e => e[1]) || [];
};
const getOption = () => {
let xData = [props.inputData?.x1 || []];
let yData = sortData(props.inputData);
let length = Math.min(...xData.map(e => e.length), ...yData.map(e => e.length));
let source = [['product', ...props.data.options?.yNames]];
const chartOption = {
title: {
text: props.data.options.title || '设备运行数量',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
left: '0',
top: '5%'
},
legend: {
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
label: {
show: true,
backgroundColor: '#fff',
color: '#000',
borderColor: 'rgba(0,0,0,0)',
shadowColor: 'rgba(0,0,0,0)',
shadowOffsetY: 0
},
lineStyle: {
width: 0
}
},
backgroundColor: '#fff',
textStyle: {
color: '#000'
},
padding: [10, 10],
extraCssText: 'box-shadow: 1px 0 2px 0 rgba(163,163,163,0.5)'
},
grid: {
top: '30%',
bottom: '10%'
},
xAxis: [
{
type: 'category',
axisLine: {
lineStyle: {
color: '#DCE2E8'
}
},
axisTick: {
show: true
},
axisLabel: {
interval: 0,
textStyle: {
color: '#fff'
},
// x
fontSize: 12,
// margin:x
margin: 3
},
axisPointer: {
label: {
padding: [0, 0, 0, 0],
margin: 0,
//
fontSize: 12
}
},
boundaryGap: true
}
],
yAxis: {
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#fff'
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
},
splitLine: {
show: false
}
},
series: props.data.options?.yNames.map(() => {
return {
type: 'line',
symbolSize: 1,
symbol: 'circle',
smooth: false,
yAxisIndex: 0,
showSymbol: false,
lineStyle: {
width: 1,
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: '#9effff'
},
{
offset: 1,
color: '#9E87FF'
}
]),
shadowColor: 'rgba(158,135,255, 0.3)',
shadowBlur: 10,
shadowOffsetY: 20
},
itemStyle: {
normal: {
color: colorList[0],
borderColor: colorList[0]
}
}
};
})
};
Array(length).fill(0).forEach((_, i) => {
let item = [];
xData.forEach(e => {
item.push(e[i]);
});
yData.forEach(e => {
item.push(e[i]);
});
source.push(item);
});
return {
...chartOption,
dataset: {
source
}
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg' // 使 SVG
});
chart && chart.setOption(getOption(), true);
});
watch(() => [JSON.parse(JSON.stringify(props.inputData)), JSON.parse(JSON.stringify(props.data.options))], (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart && chart.setOption(getOption(), true);
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']);
const resize = (e) => {
chart.resize();
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
width: 100%;
height: 100%;
}
</style>

@ -21,6 +21,7 @@ import { NodeResizer } from '@vue-flow/node-resizer';
import { Connection } from '@element-plus/icons-vue';
import { Handle, Position } from '@vue-flow/core';
import axios from 'axios';
import request from '@/utils/request.js';
const props = defineProps({
isView: {
@ -82,12 +83,18 @@ const getOutputData = () => {
(props.data.customData.inputData || []).forEach((item) => {
params[item.name] = props.inputData[item.name];
});
axios.request({
request({
method: props.data.customData.method,
url: props.data.customData.url,
params: props.data.customData.method === 'get' ? params : '',
data: props.data.customData.method === 'post' ? params : ''
}).then(res => {
// axios.request({
// method: props.data.customData.method,
// url: props.data.customData.url,
// params: props.data.customData.method === 'get' ? params : '',
// data: props.data.customData.method === 'post' ? params : ''
// }).then(res => {
let output = {};
props.data.customData.outputData.forEach(item => {
output[item.name] = parseData(res.data, item.tier);

@ -21,6 +21,7 @@ import { NodeResizer } from '@vue-flow/node-resizer';
import { Connection } from '@element-plus/icons-vue';
import { Handle, Position } from '@vue-flow/core';
import axios from 'axios';
import request from '@/utils/request.js';
const props = defineProps({
isView: {
@ -57,11 +58,16 @@ const service = axios.create({
timeout: 10000
});
const getOutputData = () => {
service({
request({
method: 'post',
url: '/test/getDevice',
data: props.inputData
}).then(res => {
// service({
// method: 'post',
// url: '/test/getDevice',
// data: props.inputData
// }).then(res => {
props.data.outputData = {
time: res.data?.data?.map(e => e.time),
value: res.data?.data?.map(e => e.value)

@ -0,0 +1,62 @@
<template>
<div
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<NodeResizer color="#fff" v-if="!props.isView && !props.isHideHandle && props.selected"
@resize="resize" />
<div class="custom-node"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<el-image style="width: 100%; height: 100%"
:src="props.inputData?.imgSrc ||props.data.options.imgSrc || 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'"
fit="contain" />
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
const props = defineProps({
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const emit = defineEmits(['resize']);
const resize = (e) => {
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
position: absolute;
}
</style>

@ -47,7 +47,7 @@ const props = defineProps({
}
});
watch(() => input.value, () => {
watch(() => JSON.parse(JSON.stringify(input.value)), () => {
if (props.data?.options?.field) {
props.data.outputData[props.data.options.field] = input.value;
}

@ -65,7 +65,12 @@ const convertData = () => {
});
props.data.outputData = res;
};
watch(() => props.inputData, (obj1, obj2) => {
watch(() => JSON.parse(JSON.stringify(props.inputData)), (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
convertData();
}
}, { deep: true, immediate: true });
watch(() => JSON.parse(JSON.stringify(props.data.options.dataMap)), (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
convertData();
}

@ -0,0 +1,62 @@
<template>
<div
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<NodeResizer color="#fff" v-if="!props.isView && !props.isHideHandle && props.selected"
@resize="resize" />
<div class="custom-node"
:style="{textAlign:props.data.options.align,width:props.dimensions.width+'px',lineHeight:props.dimensions.height+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<span
:style="{color:props.data.options.color}">{{ props.inputData?.text || props.data.options.text
}}</span>
</div>
<Handle v-if="!props.isView" :id="`${props.id}.-t`" type="target" :position="Position.Left" />
</div>
</template>
<script setup>
import { defineEmits, defineProps, ref } from 'vue';
import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core';
const props = defineProps({
isView: {
type: Boolean,
required: false
},
inputData: {
type: Object,
required: false
},
id: {
type: String,
required: true
},
isHideHandle: {
type: Boolean,
required: false
},
selected: {
type: Boolean,
required: false
},
data: {
type: Object,
required: true
},
dimensions: {
type: Object,
required: true
}
});
const emit = defineEmits(['resize']);
const resize = (e) => {
emit('resize', e, props.id);
};
</script>
<style scoped>
.custom-node {
position: absolute;
}
</style>

@ -56,7 +56,7 @@ const props = defineProps({
}
});
watch(() => value.value, () => {
watch(() => JSON.parse(JSON.stringify(value.value)), () => {
if (props.data?.options?.startTimeId) {
props.data.outputData[props.data.options.startTimeId || 'startTime'] = value.value?.[0];
}

@ -1,7 +1,8 @@
<template>
<div style="pointer-events: none">
<div class="custom-node">
<div class="area" style="width:1910px;height:970px;border: 1px solid #fff"></div>
<div class="area"
:style='`width:${props.pageData.width || "1920px"};height:${props.pageData.height || "1080px"};border: 1px solid #fff;background-image:url(${props.pageData.bg})`'></div>
</div>
</div>
</template>
@ -9,8 +10,16 @@
<script setup>
import { defineProps } from 'vue';
const props = defineProps({});
const props = defineProps({
pageData: {
type: Object,
required: false
}
});
</script>
<style scoped>
.area {
background-repeat: no-repeat;
background-size: 100% 100%;
}
</style>

@ -9,8 +9,12 @@ const getId = (type) => {
const getOption = (e) => {
if (e === 'line') {
return {};
if (e === 'line' || e === 'multiLines') {
return { title: '', yNames: [] };
} else if (e === 'bar' || e === 'multiBars') {
return { title: '', yNames: [] };
} else if (e === 'curve' || e === 'multiCurves') {
return { title: '', yNames: [] };
} else if (e === 'data') {
return {};
} else if (e === 'map') {
@ -21,6 +25,10 @@ const getOption = (e) => {
return { startTimeId: 'startTime', endTimeId: 'endTime' };
} else if (e === 'customBoard') {
return { option: '', xData: [], yData: [], yName: [] };
} else if (e === 'text') {
return { text: '文字', align: '', color: '#fff' };
} else if (e === 'img') {
return { imgSrc: '' };
} else {
return {};
}
@ -71,7 +79,7 @@ const tool = () => {
type: nodeType.value,
dimensions,
position,
data: { label: nodeId, options: getOption(nodeType.value), outputData: {}, customData: customData.value }
data: { options: getOption(nodeType.value), outputData: {}, customData: customData.value }
};

@ -1,5 +1,6 @@
<template>
<div class="content" :style="{width:area.width+'px',height: area.height+'px'}">
<div class="content"
:style='`width:${area.width || "1920px"};height:${area.height || "1080px"};background-image:url(${area.bg})`'>
<div v-for="i in nodes" class="node" :style="{left:i.position?.x+'px',top: i.position?.y+'px'}">
<template v-if="i.type === 'customBoard'">
<CustomBoardNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></CustomBoardNode>
@ -10,6 +11,21 @@
<template v-if="i.type === 'line'">
<LineNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></LineNode>
</template>
<template v-if="i.type === 'multiLines'">
<MultiLinesNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></MultiLinesNode>
</template>
<template v-if="i.type === 'curve'">
<CurveNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></CurveNode>
</template>
<template v-if="i.type === 'multiCurves'">
<MultiCurvesNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></MultiCurvesNode>
</template>
<template v-if="i.type === 'bar'">
<BarNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></BarNode>
</template>
<template v-if="i.type === 'multiBars'">
<MultiBarsNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></MultiBarsNode>
</template>
<template v-if="i.type === 'data'">
<DataNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></DataNode>
</template>
@ -22,6 +38,12 @@
<template v-if="i.type === 'map'">
<MapNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></MapNode>
</template>
<template v-if="i.type === 'text'">
<TextNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></TextNode>
</template>
<template v-if="i.type === 'img'">
<ImgNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></ImgNode>
</template>
</div>
</div>
@ -30,510 +52,21 @@
import TimeNode from './nodes/form/timeNode.vue';
import DataNode from './nodes/data/dataNode.vue';
import LineNode from './nodes/board/lineNode.vue';
import MultiLinesNode from './nodes/board/multiLinesNode.vue';
import CurveNode from './nodes/board/curveNode.vue';
import MultiCurvesNode from './nodes/board/multiCurvesNode.vue';
import BarNode from './nodes/board/barNode.vue';
import MultiBarsNode from './nodes/board/multiBarsNode.vue';
import InputNode from './nodes/form/inputNode.vue';
import MapNode from './nodes/form/mapNode.vue';
import AreaNode from './nodes/other/areaNode.vue';
import CustomBoardNode from './nodes/board/customBoardNode.vue';
import CustomDataNode from './nodes/data/customDataNode.vue';
import TextNode from './nodes/form/textNode.vue';
import ImgNode from './nodes/form/imgNode.vue';
const nodes = ref([
{
'id': 'area_474cfc79_4011_495e_9fb3_5e268ad09cfe',
'type': 'area',
'dimensions': { 'width': 1910, 'height': 970 },
'computedPosition': { 'x': 0, 'y': 0, 'z': 0 },
'handleBounds': { 'source': null, 'target': null },
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 0, 'y': 0 },
'data': { 'label': 'area_474cfc79_4011_495e_9fb3_5e268ad09cfe' },
'events': {},
'name': 'area'
}, {
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'type': 'time',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 30.66721629102227, 'y': 21.424672395499982, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-s',
'type': 'source',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-t',
'type': 'target',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 30.66721629102227, 'y': 21.424672395499982 },
'data': {
'label': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'options': { 'startTimeId': 'startTime', 'endTimeId': 'endTime' },
'outputData': {}
},
'events': {},
'name': 'time'
}, {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'customData',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 202.86901500385937, 'y': -165.53728049272314, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'type': 'source',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-t',
'type': 'target',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 202.86901500385937, 'y': -165.53728049272314 },
'data': {
'label': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'options': {},
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
},
'customData': {
'inputData': [{ 'name': 'startTime' }, { 'name': 'endTime' }],
'outputData': [{ 'name': 'time', 'remark': '时间', 'tier': 'data,map%time' }, {
'name': 'value',
'remark': '值',
'tier': 'data,map%value'
}],
'name': '测试2',
'url': 'http://localhost:3000/test/getDevice',
'method': 'post'
}
},
'events': {},
'name': 'customData'
}, {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'map',
'dimensions': { 'width': 100, 'height': 30 },
'computedPosition': { 'x': 384.9109165002872, 'y': -88.45647535459605, 'z': 1000 },
'handleBounds': {
'source': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'type': 'source',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'right',
'x': 97,
'y': 12,
'width': 6,
'height': 6
}],
'target': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-t',
'type': 'target',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'left',
'x': -3,
'y': 12,
'width': 6,
'height': 6
}]
},
'selected': true,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 384.9109165002872, 'y': -88.45647535459605 },
'data': {
'label': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'options': { 'dataMap': [{ 'source': 'value', 'target': 'y1' }, { 'source': 'time', 'target': 'x1' }] },
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
}
},
'events': {},
'name': 'map'
}, {
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'type': 'line',
'dimensions': { 'width': 300, 'height': 150 },
'computedPosition': { 'x': 144.31114508758668, 'y': 202.1298087493107, 'z': 0 },
'handleBounds': {
'source': null,
'target': [{
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772.-t',
'type': 'target',
'nodeId': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'position': 'left',
'x': -3,
'y': 71.99951171875,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 144.31114508758668, 'y': 202.1298087493107 },
'data': { 'label': 'line_650b8add_cbce_402a_a8e5_2c5769526772', 'options': {}, 'outputData': {} },
'events': {},
'name': 'line'
}]);
const edges = ref([
{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b---customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'bezier',
'source': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'target': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'sourceHandle': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-s',
'data': {},
'events': {},
'label': '',
'animated': true,
'markerEnd': 'arrowclosed',
'style': { 'stroke': '#409EFF' },
'sourceNode': {
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'type': 'time',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 30.66721629102227, 'y': 21.424672395499982, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-s',
'type': 'source',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b.-t',
'type': 'target',
'nodeId': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 30.66721629102227, 'y': 21.424672395499982 },
'data': {
'label': 'time_a3f53d63_2a0c_4be6_9530_0ee8566e0e2b',
'options': { 'startTimeId': 'startTime', 'endTimeId': 'endTime' },
'outputData': {}
},
'events': {},
'name': 'time'
},
'targetNode': {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'customData',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 202.86901500385937, 'y': -165.53728049272314, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'type': 'source',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-t',
'type': 'target',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 202.86901500385937, 'y': -165.53728049272314 },
'data': {
'label': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'options': {},
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
},
'customData': {
'inputData': [{ 'name': 'startTime' }, { 'name': 'endTime' }],
'outputData': [{ 'name': 'time', 'remark': '时间', 'tier': 'data,map%time' }, {
'name': 'value',
'remark': '值',
'tier': 'data,map%value'
}],
'name': '测试2',
'url': 'http://localhost:3000/test/getDevice',
'method': 'post'
}
},
'events': {},
'name': 'customData'
},
'sourceX': 133.66721629102227,
'sourceY': 71.42467239549998,
'targetX': 199.86901500385937,
'targetY': -115.53728049272314
}, {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699---map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'bezier',
'source': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'target': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'sourceHandle': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'data': {},
'events': {},
'label': '',
'animated': true,
'markerEnd': 'arrowclosed',
'style': { 'stroke': '#409EFF' },
'sourceNode': {
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'type': 'customData',
'dimensions': { 'width': 100, 'height': 100 },
'computedPosition': { 'x': 202.86901500385937, 'y': -165.53728049272314, 'z': 0 },
'handleBounds': {
'source': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-s',
'type': 'source',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'right',
'x': 97,
'y': 47,
'width': 6,
'height': 6
}],
'target': [{
'id': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699.-t',
'type': 'target',
'nodeId': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'position': 'left',
'x': -3,
'y': 47,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 202.86901500385937, 'y': -165.53728049272314 },
'data': {
'label': 'customData_de7fe6a5_adeb_4c96_9cf4_0c76df770699',
'options': {},
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
},
'customData': {
'inputData': [{ 'name': 'startTime' }, { 'name': 'endTime' }],
'outputData': [{ 'name': 'time', 'remark': '时间', 'tier': 'data,map%time' }, {
'name': 'value',
'remark': '值',
'tier': 'data,map%value'
}],
'name': '测试2',
'url': 'http://localhost:3000/test/getDevice',
'method': 'post'
}
},
'events': {},
'name': 'customData'
},
'targetNode': {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'map',
'dimensions': { 'width': 100, 'height': 30 },
'computedPosition': { 'x': 384.9109165002872, 'y': -88.45647535459605, 'z': 1000 },
'handleBounds': {
'source': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'type': 'source',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'right',
'x': 97,
'y': 12,
'width': 6,
'height': 6
}],
'target': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-t',
'type': 'target',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'left',
'x': -3,
'y': 12,
'width': 6,
'height': 6
}]
},
'selected': true,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 384.9109165002872, 'y': -88.45647535459605 },
'data': {
'label': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'options': { 'dataMap': [{ 'source': 'value', 'target': 'y1' }, { 'source': 'time', 'target': 'x1' }] },
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
}
},
'events': {},
'name': 'map'
},
'sourceX': 305.8690150038594,
'sourceY': -115.53728049272314,
'targetX': 381.9109165002872,
'targetY': -73.45647535459605
}, {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3---line_650b8add_cbce_402a_a8e5_2c5769526772',
'type': 'bezier',
'source': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'target': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'sourceHandle': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'data': {},
'events': {},
'label': '',
'animated': true,
'markerEnd': 'arrowclosed',
'style': { 'stroke': '#409EFF' },
'sourceNode': {
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'type': 'map',
'dimensions': { 'width': 100, 'height': 30 },
'computedPosition': { 'x': 384.9109165002872, 'y': -88.45647535459605, 'z': 1000 },
'handleBounds': {
'source': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-s',
'type': 'source',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'right',
'x': 97,
'y': 12,
'width': 6,
'height': 6
}],
'target': [{
'id': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3.-t',
'type': 'target',
'nodeId': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'position': 'left',
'x': -3,
'y': 12,
'width': 6,
'height': 6
}]
},
'selected': true,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 384.9109165002872, 'y': -88.45647535459605 },
'data': {
'label': 'map_54688bd0_db73_4f96_be55_c72beb0b33d3',
'options': { 'dataMap': [{ 'source': 'value', 'target': 'y1' }, { 'source': 'time', 'target': 'x1' }] },
'outputData': {
'time': ['2025-01-01 00:00:00', '2025-01-03 00:00:00', '2025-01-05 00:00:00'],
'value': [20, 30, 40]
}
},
'events': {},
'name': 'map'
},
'targetNode': {
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'type': 'line',
'dimensions': { 'width': 300, 'height': 150 },
'computedPosition': { 'x': 144.31114508758668, 'y': 202.1298087493107, 'z': 0 },
'handleBounds': {
'source': null,
'target': [{
'id': 'line_650b8add_cbce_402a_a8e5_2c5769526772.-t',
'type': 'target',
'nodeId': 'line_650b8add_cbce_402a_a8e5_2c5769526772',
'position': 'left',
'x': -3,
'y': 71.99951171875,
'width': 6,
'height': 6
}]
},
'selected': false,
'dragging': false,
'resizing': false,
'initialized': false,
'isParent': false,
'position': { 'x': 144.31114508758668, 'y': 202.1298087493107 },
'data': { 'label': 'line_650b8add_cbce_402a_a8e5_2c5769526772', 'options': {}, 'outputData': {} },
'events': {},
'name': 'line'
},
'sourceX': 487.9109165002872,
'sourceY': -73.45647535459605,
'targetX': 141.31114508758668,
'targetY': 277.1293204680607
}]);
const nodes = ref([]);
const edges = ref([]);
const area = ref({
width: 1910,
height: 970
@ -555,11 +88,18 @@ const getInputData = (e) => {
});
return outputData;
};
onMounted(() => {
nodes.value = JSON.parse(localStorage.getItem('NODES') || '[]');
edges.value = JSON.parse(localStorage.getItem('EDGES') || '[]');
area.value = JSON.parse(localStorage.getItem('PAGEDATA'));
});
</script>
<style lang="less" scoped>
.content {
position: absolute;
background-color: #000000;
background-repeat: no-repeat;
background-size: 100% 100%;
}
.node {

@ -403,7 +403,7 @@ const initFormData: DmsBillsFaultInstanceForm = {
}
}
const data = reactive<PageData<DmsBillsFaultInstanceForm, DmsBillsFaultInstanceQuery>>({
form: {...initFormData},
queryParams: {

@ -411,7 +411,7 @@ const initFormData: DmsBillsFaultInstanceForm = {
componentsPartsId:undefined,
parts:undefined,
}
}
const data = reactive<PageData<DmsBillsFaultInstanceForm, DmsBillsFaultInstanceQuery>>({

Loading…
Cancel
Save