修改组态化

master
夜笙歌 2 months ago
parent af3e5f053d
commit 7ad9739095

@ -5,6 +5,9 @@
<el-button type="primary" @click="constructionData"></el-button>
<el-dialog v-model="constructBoardFormVisible" title="构造图表" width="500">
<el-form :model="constructBoardForm">
<el-form-item label="图表名称" prop="name">
<el-input v-model="constructBoardForm.name" />
</el-form-item>
<el-form-item label="图表配置">
<el-button type="primary" plain @click="importJson">json</el-button>
<el-input type="textarea" v-model="constructBoardForm.option" />
@ -124,7 +127,8 @@
<script setup lang="ts">
import axios from 'axios';
import request from '@/utils/request';
import { isD } from './tool.js';
import { options } from './tool.js';
import * as echarts from 'echarts';
const comparisonTable = {
starttime: '开始时间',
@ -139,10 +143,6 @@ function isJSON(str) {
JSON.parse(str);
return true;
} catch (e) {
ElMessage({
message: '格式错误',
type: 'warning'
});
return false;
}
}
@ -159,7 +159,211 @@ const constructBoard = () => {
constructBoardFormVisible.value = true;
};
const createBoard = () => {
// let data = `{
// backgroundColor: '#000',
// tooltip: {
// trigger: 'axis'
// },
// legend: {
// top: '5%',
// right: '2%',
// itemGap: 20,
// icon: 'circle',
// itemWidth: 10,
// itemHeight: 10,
// textStyle: {
// color: '#C5D6E6'
// }
// },
// grid: {
// left: '1%',
// right: '2%',
// bottom: '5%',
// containLabel: true
// },
// xAxis: [
// {
// type: 'category',
// data: ['2023-01', '2023-02', '2023-03', '2023-04', '2023-05', '2023-06'],
// axisLabel: {
// textStyle: {
// color: '#C5D6E6'
// }
// },
// axisLine: {
// lineStyle: {
// color: '#D9E7FF'
// }
// }
// }
// ],
// yAxis: [
// {
// type: 'value',
// splitNumber: 4,
// splitLine: {
// lineStyle: {
// color: 'rgba(217, 231, 255, 0.3)'
// }
// },
// axisLabel: {
// textStyle: {
// color: '#C5D6E6'
// }
// },
// axisLine: {
// show: false,
// lineStyle: {
// color: '#333'
// }
// },
// nameTextStyle: {
// color: '#999'
// },
// splitArea: {
// show: false
// }
// }
// ],
// series: [
// {
// name: '',
// type: 'line',
// lineStyle: {
// normal: {
// width: 2,
// color: 'rgba(15, 222, 255, 1)',
// shadowColor: 'rgba(72,216,191, 0.3)',
// shadowBlur: 10,
// shadowOffsetY: 20
// }
// },
// areaStyle: {
// normal: {
// color: new echarts.graphic.LinearGradient(
// 0,
// 0,
// 0,
// 1,
// [
// {
// offset: 0,
// color: '#0FDEFF'
// },
// {
// offset: 1,
// color: 'rgba(15, 222, 255, 0)'
// }
// ],
// false
// )
// }
// },
// itemStyle: {
// color: 'rgba(15, 222, 255, 1)'
// },
// smooth: true,
// symbol: 'none'
// },
// {
// name: '',
// type: 'line',
// lineStyle: {
// normal: {
// width: 2,
// color: 'rgba(0, 144, 255, 1)'
// }
// },
// areaStyle: {
// normal: {
// color: new echarts.graphic.LinearGradient(
// 0,
// 0,
// 0,
// 1,
// [
// {
// offset: 0,
// color: 'rgba(0, 144, 255, 1)'
// },
// {
// offset: 1,
// color: 'rgba(0, 144, 255, 0)'
// }
// ],
// false
// )
// }
// },
// itemStyle: {
// color: 'rgba(0, 144, 255, 1)'
// },
// smooth: true,
// symbol: 'none'
// },
// {
// name: '',
// type: 'line',
// lineStyle: {
// normal: {
// width: 2,
// color: 'rgba(255, 208, 59, 1)'
// }
// },
// areaStyle: {
// normal: {
// color: new echarts.graphic.LinearGradient(
// 0,
// 0,
// 0,
// 1,
// [
// {
// offset: 0,
// color: 'rgba(255, 208, 59, 1)'
// },
// {
// offset: 1,
// color: 'rgba(255, 208, 59, 0)'
// }
// ],
// false
// )
// }
// },
// itemStyle: {
// color: 'rgba(255, 208, 59, 1)'
// },
// smooth: true,
// symbol: 'none'
// }
// ]
// }`;
let fun = new Function('echarts', `return ${constructBoardForm.value.option}`);
let option = constructBoardForm.value.option;
console.log();
try {
option = fun(echarts) || null;
} catch (e) {
}
if (isJSON(constructBoardForm.value.option)) {
constructBoardFormVisible.value = false;
let nowData = JSON.parse(localStorage.getItem('BOARDNODE') || '[]');
nowData.push(constructBoardForm.value);
localStorage.setItem('BOARDNODE', JSON.stringify(nowData));
} else if (option) {
constructBoardFormVisible.value = false;
let nowData = JSON.parse(localStorage.getItem('BOARDNODE') || '[]');
nowData.push({ ...constructBoardForm.value, option: JSON.stringify(option) });
localStorage.setItem('BOARDNODE', JSON.stringify(nowData));
} else {
ElMessage({
message: '格式错误',
type: 'warning'
});
}
};
const constructionData = () => {
constructionDataForm.value = { inputData: [], outputData: [] };
@ -198,7 +402,7 @@ const findTier = () => {
params[item.name] = item.test;
});
(isD ? request : axios.request)({
(options.isD ? request : axios.request)({
method: constructionDataForm.value.method,
url: constructionDataForm.value.url,
params: constructionDataForm.value.method === 'get' ? params : '',

@ -2,124 +2,129 @@
<div class="leftPanel">
<el-tabs v-model="leftPanelState" class="demo-tabs" type="border-card">
<el-tab-pane label="图表组件" name="1">
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'line')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
折线
<div class="moduleText">折线</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" 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>
多折线
<div class="moduleText">多折线</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" 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>
曲线
<div class="moduleText">曲线</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" 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>
多曲线
<div class="moduleText">多曲线
</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" 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>
柱状图
<div class="moduleText">柱状图</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" 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>
多柱状图
<div class="moduleText">多柱状图
</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'customBoard')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
<template v-for="i in customBoard">
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'customBoard',i)"
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
自定义图表1
<div class="moduleText">{{ i.name }}</div>
</el-card>
</template>
</el-tab-pane>
<el-tab-pane label="数据源" name="2">
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'data')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
设备数据
<div class="moduleText">设备数据</div>
</el-card>
<template v-for="i in customData">
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'customData',i)"
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
{{ i.name }}
<div class="moduleText">{{ i.name }}</div>
</el-card>
</template>
</el-tab-pane>
<el-tab-pane label="表单组件" name="3">
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'map')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
映射
<div class="moduleText">映射</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" 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>
文字
<div class="moduleText">文字</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'inputNode')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
输入框
<div class="moduleText">输入框</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'time')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
<template #header>
<StarFilled />
</template>
时间
<div class="moduleText">时间</div>
</el-card>
<el-card style="width: 80px" shadow="never" :draggable="true"
<el-card class="moduleCard" 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>
图片
<div class="moduleText">图片</div>
</el-card>
</el-tab-pane>
</el-tabs>
@ -363,8 +368,10 @@ const nodes = ref([{
}]);
const edges = ref([]);
const customData = ref([]);
const customBoard = ref([]);
onMounted(async () => {
customData.value = JSON.parse(localStorage.getItem('DATANODE') || '[]');
customBoard.value = JSON.parse(localStorage.getItem('BOARDNODE') || '[]');
await nextTick();
nodes.value = reactive(JSON.parse(localStorage.getItem('NODES') || '[]'));
edges.value = JSON.parse(localStorage.getItem('EDGES') || '[]');
@ -496,6 +503,50 @@ const setPageData = () => {
pointer-events: none !important;
}
: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;
}
.moduleCard {
width: 80px;
vertical-align: top;
:deep(.el-card__body) {
height: 40px;
padding: 0 !important;
}
.moduleText {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
text-align: center;
font-size: 12px;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
.leftPanel {
position: absolute;
top: 0;
@ -552,25 +603,4 @@ const setPageData = () => {
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>

@ -1,41 +1,21 @@
<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" />
<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 class="info">
<el-icon color="#fff" @click="openMsgBox">
<InfoFilled />
</el-icon>
</div>
<el-dialog
v-model="MsgBoxVisible"
width="500"
title="提示"
append-to-body
>
<span>This is a message</span>
<template #footer>
<div class="dialog-footer">
<el-button @click="MsgBoxVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { InfoFilled } from '@element-plus/icons-vue';
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 MsgBoxVisible = ref(false);
const props = defineProps({
isView: {
type: Boolean,
@ -69,170 +49,60 @@ const props = defineProps({
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 = JSON.parse(props.data.customData.option || '{}');
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 {
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'
...chartOption,
dataset: {
source
}
},
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',
data: props.inputData?.time || [],
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: [
{
name: '数量',
type: 'line',
data: props.inputData?.value || [],
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]
}
}
}
]
};
};
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);
};
const openMsgBox = () => {
MsgBoxVisible.value = true;
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons');
chart.setOption(getOption(), true);
});
watch(() => JSON.parse(JSON.stringify(props.inputData)), () => {
chart.setOption(getOption(), true);
}, { deep: true });
</script>
<style lang="less" scoped>
<style scoped>
.custom-node {
width: 100%;
height: 100%;
}
.info {
position: absolute;
top: 0;
right: 0;
}
</style>

@ -22,7 +22,7 @@ import { Connection } from '@element-plus/icons-vue';
import { Handle, Position } from '@vue-flow/core';
import request from '@/utils/request';
import axios from 'axios';
import { isD } from '../../tool.js';
import { options } from '../../tool.js';
const props = defineProps({
isView: {
@ -84,7 +84,7 @@ const getOutputData = () => {
(props.data.customData.inputData || []).forEach((item) => {
params[item.name] = props.inputData[item.name];
});
(isD ? request : axios.request)({
(options.isD ? request : axios.request)({
method: props.data.customData.method,
url: props.data.customData.url,
params: props.data.customData.method === 'get' ? params : '',

@ -22,7 +22,7 @@ import { Connection } from '@element-plus/icons-vue';
import { Handle, Position } from '@vue-flow/core';
import axios from 'axios';
import request from '@/utils/request';
import { isD } from '../../tool.js';
import { options } from '../../tool.js';
const props = defineProps({
isView: {
@ -59,7 +59,7 @@ const service = axios.create({
timeout: 10000
});
const getOutputData = () => {
(isD ? request : service)({
(options.isD ? request : service)({
method: 'post',
url: '/test/getDevice',
data: props.inputData

@ -15,6 +15,8 @@ const getOption = (e) => {
return { title: '', yNames: [] };
} else if (e === 'curve' || e === 'multiCurves') {
return { title: '', yNames: [] };
} else if (e === 'customBoard') {
return { title: '', yNames: [] };
} else if (e === 'data') {
return {};
} else if (e === 'map') {
@ -23,8 +25,6 @@ const getOption = (e) => {
return { field: '' };
} else if (e === 'time') {
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') {
@ -109,4 +109,6 @@ const tool = () => {
};
};
export default tool;
export const isD = false;
export const options = {
isD: false
};

Loading…
Cancel
Save