修改看板

master
夜笙歌 4 weeks ago
parent a690458b08
commit bec1fe819b

@ -44,6 +44,15 @@
</template>
<div class="moduleText">柱状图</div>
</el-card>
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'backgroundBar')"
: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 class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'multiBars')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
@ -160,6 +169,14 @@
</template>
<div class="moduleText">图片</div>
</el-card>
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'icon')" :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 class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'video')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
:body-style="{padding:'4px 0'}">
@ -249,6 +266,10 @@
<BarNode :inputData=getInputData(barNodeProps.id) v-bind="barNodeProps"
@resize="resize"></BarNode>
</template>
<template #node-backgroundBar="backgroundBarNodeProps">
<BackgroundBarNode :inputData=getInputData(backgroundBarNodeProps.id) v-bind="backgroundBarNodeProps"
@resize="resize"></BackgroundBarNode>
</template>
<template #node-multiBars="multiBarsNodeProps">
<MultiBarsNode :inputData=getInputData(multiBarsNodeProps.id) v-bind="multiBarsNodeProps"
@resize="resize"></MultiBarsNode>
@ -278,6 +299,10 @@
<ImgNode :inputData=getInputData(imgNodeProps.id) v-bind="imgNodeProps"
@resize="resize"></ImgNode>
</template>
<template #node-icon="iconNodeProps">
<IconNode :inputData=getInputData(iconNodeProps.id) v-bind="iconNodeProps"
@resize="resize"></IconNode>
</template>
<template #node-video="videoNodeProps">
<VideoNode :inputData=getInputData(videoNodeProps.id) v-bind="videoNodeProps"
@resize="resize"></VideoNode>
@ -445,12 +470,49 @@
<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('backgroundColor')">
<el-color-picker v-model="nodeAttrForm.backgroundColor" show-alpha />
</el-form-item>
<el-form-item label="时间戳颜色" v-if="Object.keys(nodeAttrForm).includes('timestampColor')">
<el-color-picker v-model="nodeAttrForm.timestampColor" 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('icon')">
<el-popover
placement="bottom"
:width="400"
trigger="click"
>
<template #reference>
<el-input readonly v-model="nodeAttrForm.icon" style="width: 100%" placeholder="选择图标">
<template #prepend>
<el-button :icon="icon[nodeAttrForm.icon]" />
</template>
</el-input>
</template>
<el-input v-model="selectIconInput" style="width: 100%" placeholder="筛选图标" />
<div style="height: 50vh;overflow-y: auto">
<!-- <el-tooltip-->
<!-- class="box-item"-->
<!-- effect="dark"-->
<!-- :content="i"--
<!-- placement="bottom"-->
<!-- v-for="i in icons.filter(e=>e.includes(selectIconInput))"-->
<!-- >-->
<el-button :icon="icon[i]" @click="nodeAttrForm.icon = i"
:type="nodeAttrForm.icon === i ? 'primary':''"
v-for="i in icons.filter(e=>e.includes(selectIconInput))"
style="margin-top: 4px;margin-left: 0;margin-right: 4px;" />
<!-- </el-tooltip>-->
</div>
</el-popover>
</el-form-item>
<el-form-item label="图标路径" v-if="Object.keys(nodeAttrForm).includes('iconSrc')">
<el-input v-model="nodeAttrForm.imgSrc" style="width: 100%" />
</el-form-item>
<el-form-item label="视频路径" v-if="Object.keys(nodeAttrForm).includes('videoSrc')">
<el-input v-model="nodeAttrForm.videoSrc" style="width: 100%" />
</el-form-item>
@ -785,6 +847,7 @@ import { globalHeaders } from '@/utils/request';
import { useRouter } from 'vue-router';
import { v4 as uuidv4 } from 'uuid';
import { Check, Close, Plus, Setting, StarFilled, Delete } from '@element-plus/icons-vue';
import * as icon from '@element-plus/icons-vue';
import { MarkerType, useVueFlow, VueFlow } from '@vue-flow/core';
import { Background } from '@vue-flow/background';
import LineNode from './nodes/board/lineNode.vue';
@ -792,6 +855,7 @@ 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 BackgroundBarNode from './nodes/board/backgroundBarNode.vue';
import MultiBarsNode from './nodes/board/multiBarsNode.vue';
import StaticDataNode from './nodes/data/staticDataNode.vue';
import CustomBoardNode from './nodes/board/customBoardNode.vue';
@ -805,6 +869,7 @@ 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 IconNode from './nodes/form/iconNode.vue';
import VideoNode from './nodes/form/videoNode.vue';
import TimelineNode from './nodes/form/timelineNode.vue';
import AreaNode from './nodes/other/areaNode.vue';
@ -831,6 +896,8 @@ const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadFileUrl = ref(baseUrl + '/resource/oss/upload'); //
const headers = ref(globalHeaders());
const icons = ref([]);
const selectIconInput = ref('');
const loading = ref(false)
const pageSettingVisible = ref(false);
const pageSettingForm = ref({});
@ -855,6 +922,7 @@ const customData = ref([]);
const customBoard = ref([]);
const boardData = ref({});
onMounted(async () => {
icons.value = Object.keys(icon);
loading.value = true
getDataSourceList({
pageNum: 1,
@ -877,7 +945,7 @@ onMounted(async () => {
.then((res) => {
let data = res.data;
boardData.value = data;
nodes.value = data.designPagePointList?.length !== 0 ? data.designPagePointList.map(e => {
nodes.value = data.designPagePointList?.length !== 0 ? data.designPagePointList.sort((a, b) => a.fieldOne - b.fieldOne).map(e => {
let data = {};
let savaField = ['customData', 'options'];
let dataContent = JSON.parse(e.dataContent) || {};
@ -1019,7 +1087,7 @@ const save = () => {
editBoardApi({
...boardData.value,
customContent: JSON.stringify(pageSettingForm.value),
designPagePointList: nodes.value.map(e => {
designPagePointList: nodes.value.map((e, index) => {
let data = {};
let savaField = ['customData', 'options'];
Object.keys(e.data).forEach((key) => {
@ -1040,6 +1108,7 @@ const save = () => {
pageConfigId: boardData.value.pageConfigId,
pagePointId: e.id,
pagePointName: e.name,
fieldOne: index,
width: e.dimensions.width,
height: e.dimensions.height,
posX: e.position.x,

@ -0,0 +1,275 @@
<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'}">
<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 defaultOption = {
title: {
text: props.data.options.title || '',
textStyle: {
fontSize: 12,
fontWeight: 400,
color: '#fff'
},
left: '0',
top: '5%'
},
legend: {
show: props.data?.options?.legend || false,
icon: 'circle',
top: '5%',
right: '5%',
itemWidth: 6,
itemGap: 20,
textStyle: {
color: '#fff'
}
},
tooltip: {
show: props.data?.options?.tooltip || false,
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: props.data?.options?.gridTop + '%' || '20%',
left: props.data?.options?.gridLeft + '%' || '20%',
bottom: props.data?.options?.gridBottom + '%' || '20%',
right: props.data?.options?.gridRight + '%' || '20%'
},
xAxis: [
{
name: props.data?.options?.xName || '',
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: [
{
name: props.data?.options?.yName || '',
type: 'value',
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: '#fff'
}
},
axisLabel: {
textStyle: {
color: '#fff'
}
},
splitLine: {
show: false
}
}
],
series: [
{
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
},
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
}
}
]
};
const getOption = () => {
const chartOption = {
title: {
text: props.data.options.title || ''
},
legend: {
show: props.data?.options?.legend || false
},
tooltip: {
show: props.data?.options?.tooltip || false
},
grid: {
top: props.data?.options?.gridTop + '%' || '20%',
left: props.data?.options?.gridLeft + '%' || '20%',
bottom: props.data?.options?.gridBottom + '%' || '20%',
right: props.data?.options?.gridRight + '%' || '20%'
},
xAxis: [
{
name: props.data?.options?.xName || ''
}
],
yAxis: [
{
name: props.data?.options?.yName || ''
}
],
series: [
{
backgroundStyle: {
color: props.data?.options?.backgroundColor || 'rgba(180, 180, 180, 0.2)'
}
}
]
}
;
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(parseFloat(e[i]));
});
source.push(item);
});
console.log(source);
return {
...chartOption,
dataset: {
source
}
};
};
onMounted(() => {
chart = echarts.init(chartRef.value, 'macarons', {
renderer: 'svg'
});
chart.setOption(defaultOption, 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(), false);
}
}, { deep: true, immediate: true });
watch(() => JSON.parse(JSON.stringify(props.dimensions)), (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
chart?.resize();
}
}, { 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>

@ -221,7 +221,7 @@ const getOption = () => {
item.push(e[i]);
});
yData.forEach(e => {
item.push(e[i]);
item.push(parseFloat(e[i]));
});
source.push(item);
});

@ -10,7 +10,7 @@
<swiper-slide v-for="i in list"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px'}">
<el-image :style="{width:'100%',height:'100%'}" :src="i" :fit="props.data.options.imageFit || 'contain'" />
/>
<!-- <img :src="i" style="width: 100%; height: 100%; object-fit: contain;" />-->
</swiper-slide>
</swiper-container>

@ -0,0 +1,68 @@
<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-icon style="color: #fff"
v-if="props.data.options.icon || !(props.inputData?.imgSrc ||props.data.options.iconSrc)"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',fontSize:props.dimensions.width+'px' }">
<component :is="icon[props.data.options.icon]" />
</el-icon>
<el-image style="width: 100%; height: 100%" v-if="props.inputData?.imgSrc ||props.data.options.iconSrc"
:src="props.inputData?.imgSrc ||props.data.options.iconSrc || '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';
import * as icon from '@element-plus/icons-vue';
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>

@ -13,6 +13,8 @@ const getOption = (e) => {
return { title: '', yNames: [], gridTop: 30, gridLeft: 5, gridBottom: 10, gridRight: 10, xName: '', yName: '', tooltip:true, legend:true };
} else if (e === 'bar' || e === 'multiBars') {
return { title: '', yNames: [], gridTop: 30, gridLeft: 5, gridBottom: 10, gridRight: 10, xName: '', yName: '', tooltip:true, legend:true };
} else if (e === 'backgroundBar') {
return { title: '', yNames: [], gridTop: 30, gridLeft: 5, gridBottom: 10, gridRight: 10, xName: '', yName: '', tooltip:true, legend:true,backgroundColor:'rgba(180, 180, 180, 0.2)' };
} else if (e === 'curve' || e === 'multiCurves') {
return { title: '', yNames: [], gridTop: 30, gridLeft: 5, gridBottom: 10, gridRight: 10, xName: '', yName: '', tooltip:true, legend:true };
} else if (e === 'pie' || e === 'nightingaleRoseDiagram') {
@ -35,6 +37,8 @@ const getOption = (e) => {
return { text: '文字', align: '', color: '#fff' };
} else if (e === 'img') {
return { imgSrc: '' };
} else if (e === 'icon') {
return { icon: '', iconSrc: '' };
} else if (e === 'video') {
return { videoSrc: '' };
} else if (e === 'timeline') {

@ -27,6 +27,9 @@
<template v-if="i.type === 'bar'">
<BarNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></BarNode>
</template>
<template v-if="i.type === 'backgroundBar'">
<BackgroundBarNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></BackgroundBarNode>
</template>
<template v-if="i.type === 'multiBars'">
<MultiBarsNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></MultiBarsNode>
</template>
@ -48,6 +51,9 @@
<template v-if="i.type === 'img'">
<ImgNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></ImgNode>
</template>
<template v-if="i.type === 'icon'">
<IconNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></IconNode>
</template>
<template v-if="i.type === 'video'">
<VideoNode :isView="true" :inputData=getInputData(i.id) v-bind="i"></VideoNode>
</template>
@ -84,6 +90,7 @@ 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 BackgroundBarNode from './nodes/board/backgroundBarNode.vue';
import MultiBarsNode from './nodes/board/multiBarsNode.vue';
import InputNode from './nodes/form/inputNode.vue';
import MapNode from './nodes/form/mapNode.vue';
@ -91,6 +98,7 @@ 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';
import IconNode from './nodes/form/iconNode.vue';
import VideoNode from './nodes/form/videoNode.vue';
import TimelineNode from './nodes/form/timelineNode.vue';
import PieNode from '@/views/boardGenerate/nodes/board/pieNode.vue';

Loading…
Cancel
Save