You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1457 lines
57 KiB
Vue

<template>
<div>
<div class="leftPanel">
<el-tabs v-model="leftPanelState" class="demo-tabs" type="border-card">
<el-tab-pane label="图表组件" name="1">
<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 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 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 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 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 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'}">
<template #header>
<StarFilled />
</template>
<div class="moduleText">多柱状图
</div>
</el-card>
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'pie')" :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, 'nightingaleRoseDiagram')"
: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, 'carousel')" :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 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>
<div class="moduleText">{{ i.name }}</div>
</el-card>
</template>
</el-tab-pane>
<el-tab-pane label="数据源" name="2">
<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 class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'staticData')" :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, '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 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>
<div class="moduleText">{{ i.name }}</div>
</el-card>
</template>
</el-tab-pane>
<el-tab-pane label="表单组件" name="3">
<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 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 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 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-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'}">
<template #header>
<StarFilled />
</template>
<div class="moduleText">视频</div>
</el-card>
<el-card class="moduleCard" shadow="never" :draggable="true"
@dragstart="onDragStart($event, 'timeline')" :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, 'digitalFlop')"
: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, 'table')"
: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, 'scrollTable')"
: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>
</div>
<div class="content" @drop="onDrop">
<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" v-loading="loading">
<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)"
@pane-click="logEvent('paneClick', $event)"
@node-drag-start="logEvent('nodeDrag', $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" :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-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>
</template>
<template #node-pie="pieNodeProps">
<PieNode :inputData=getInputData(pieNodeProps.id) v-bind="pieNodeProps"
@resize="resize"></PieNode>
</template>
<template #node-nightingaleRoseDiagram="nightingaleRoseDiagramNodeProps">
<NightingaleRoseDiagramNode :inputData=getInputData(nightingaleRoseDiagramNodeProps.id)
v-bind="nightingaleRoseDiagramNodeProps"
@resize="resize"></NightingaleRoseDiagramNode>
</template>
<template #node-carousel="carouselNodeProps">
<CarouselNode :inputData=getInputData(carouselNodeProps.id) v-bind="carouselNodeProps"
@resize="resize"></CarouselNode>
</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-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>
</template>
<template #node-timeline="timelineNodeProps">
<TimelineNode :inputData=getInputData(timelineNodeProps.id) v-bind="timelineNodeProps"
@resize="resize"></TimelineNode>
</template>
<template #node-digitalFlop="digitalFlopNodeProps">
<DigitalFlopNode :inputData=getInputData(digitalFlopNodeProps.id) v-bind="digitalFlopNodeProps"
@resize="resize"></DigitalFlopNode>
</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>
<template #node-staticData="staticDataNodeProps">
<StaticDataNode :inputData=getInputData(staticDataNodeProps.id) v-bind="staticDataNodeProps"
@resize="resize"></StaticDataNode>
</template>
<template #node-table="tableNodeProps">
<TableNode :inputData=getInputData(tableNodeProps.id) v-bind="tableNodeProps"
@resize="resize"></TableNode>
</template>
<template #node-scrollTable="scrollTableNodeProps">
<ScrollTableNode :inputData=getInputData(scrollTableNodeProps.id) v-bind="scrollTableNodeProps"
@resize="resize"></ScrollTableNode>
</template>
</VueFlow>
</div>
</div>
<div class="rightPanel">
<el-collapse
v-if="Object.keys(nodeDataForm).length>0 || Object.keys(nodeAttrForm).length>0|| Object.keys(customDataForm).length>0">
<el-collapse-item title="基础设置" name="1" v-if="Object.keys(nodeDataForm).length>0">
<el-form :model="nodeDataForm" label-width="auto" style="max-width: 600px">
<el-form-item label="组件名称">
<el-input v-model="nodeDataForm.name" style="width: 100%" />
</el-form-item>
<el-form-item label="x">
<el-input-number :precision="0" :step="1" v-model="nodeDataForm.position.x" style="width: 100%"
@change="updateNode(nodeDataForm.id,nodeDataForm,{ replace: true })" />
</el-form-item>
<el-form-item label="y">
<el-input-number :precision="0" :step="1" v-model="nodeDataForm.position.y" style="width: 100%"
@change="updateNode(nodeDataForm.id,nodeDataForm,{ replace: true })" />
</el-form-item>
<el-form-item label="宽度">
<el-input-number :precision="0" :step="1" v-model="nodeDataForm.dimensions.width" style="width: 100%"
@change="updateNode(nodeDataForm.id,nodeDataForm,{ replace: true })" />
</el-form-item>
<el-form-item label="高度">
<el-input-number :precision="0" :step="1" v-model="nodeDataForm.dimensions.height" style="width: 100%"
@change="updateNode(nodeDataForm.id,nodeDataForm,{ replace: true })" />
</el-form-item>
</el-form>
</el-collapse-item>
<el-collapse-item title="组件配置" name="2" v-if="Object.keys(nodeAttrForm).length>0">
<el-form :model="nodeAttrForm" label-width="auto" style="max-width: 600px">
<el-form-item label="默认内容" v-if="Object.keys(nodeAttrForm).includes('defaultInputArea')">
<el-input type="textarea" v-model="nodeAttrForm.defaultInputArea" style="width: 100%" />
</el-form-item>
<el-form-item label="默认内容" v-if="Object.keys(nodeAttrForm).includes('defaultInput')">
<el-input v-model="nodeAttrForm.defaultInput" style="width: 100%" />
</el-form-item>
<el-form-item label="默认内容" v-if="Object.keys(nodeAttrForm).includes('number')">
<el-input-number :controls="false" autocomplete="off" v-model="nodeAttrForm.number" style="width: 100%" />
</el-form-item>
<el-form-item label="x轴名称" v-if="Object.keys(nodeAttrForm).includes('xName')">
<el-input v-model="nodeAttrForm.xName" style="width: 100%" />
</el-form-item>
<el-form-item label="y轴名称" v-if="Object.keys(nodeAttrForm).includes('yName')">
<el-input v-model="nodeAttrForm.yName" style="width: 100%" />
</el-form-item>
<el-form-item label="顶部距离" v-if="Object.keys(nodeAttrForm).includes('gridTop')">
<el-input-number v-model="nodeAttrForm.gridTop" style="width: 100%" :min="0" :max="100" />
</el-form-item>
<el-form-item label="左侧距离" v-if="Object.keys(nodeAttrForm).includes('gridLeft')">
<el-input-number v-model="nodeAttrForm.gridLeft" style="width: 100%" :min="0" :max="100" />
</el-form-item>
<el-form-item label="底部距离" v-if="Object.keys(nodeAttrForm).includes('gridBottom')">
<el-input-number v-model="nodeAttrForm.gridBottom" style="width: 100%" :min="0" :max="100" />
</el-form-item>
<el-form-item label="右侧距离" v-if="Object.keys(nodeAttrForm).includes('gridRight')">
<el-input-number v-model="nodeAttrForm.gridRight" style="width: 100%" :min="0" :max="100" />
</el-form-item>
<el-form-item label="默认日期" v-if="Object.keys(nodeAttrForm).includes('defaultTime')">
<el-date-picker
v-model="nodeAttrForm.defaultTime"
type="datetimerange"
range-separator="到"
start-placeholder="开始时间"
end-placeholder="结束时间"
style="width: 100%;height: 100%"
:value-format="nodeAttrForm?.format||'YYYY/MM/DD HH:mm:ss'"
:format="nodeAttrForm?.format||'YYYY/MM/DD HH:mm:ss'"
/>
</el-form-item>
<el-form-item label="日期格式" v-if="Object.keys(nodeAttrForm).includes('format')">
<el-input v-model="nodeAttrForm.format" style="width: 100%" />
</el-form-item>
<el-form-item label="输出字段名" v-if="Object.keys(nodeAttrForm).includes('field')">
<el-input v-model="nodeAttrForm.field" />
</el-form-item>
<el-form-item label="时间戳字段名" v-if="Object.keys(nodeAttrForm).includes('timestampField')">
<el-input v-model="nodeAttrForm.timestampField" />
</el-form-item>
<el-form-item label="是否显示时间戳" v-if="Object.keys(nodeAttrForm).includes('isTimestamp')">
<el-switch v-model="nodeAttrForm.isTimestamp" />
</el-form-item>
<el-form-item label="开始时间字段名" v-if="Object.keys(nodeAttrForm).includes('startTimeId')">
<el-input v-model="nodeAttrForm.startTimeId" />
</el-form-item>
<el-form-item label="结束时间字段名" v-if="Object.keys(nodeAttrForm).includes('endTimeId')">
<el-input v-model="nodeAttrForm.endTimeId" />
</el-form-item>
<el-form-item label="数据映射" v-if="Object.keys(nodeAttrForm).includes('dataMap')">
<el-table :data="nodeAttrForm.dataMap" style="width: 100%">
<el-table-column label="源字段" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.source" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="映射字段" min-width="120">
<template #default="scope">
<el-input v-model="scope.row.target" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="操作" min-width="120">
<template #default="scope">
<el-button link type="primary" size="small" @click="nodeAttrForm.dataMap.splice(scope.$index, 1)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-button style="width: 100%" @click="nodeAttrForm.dataMap.push({})">
添加映射
</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('backgroundColor')">
<el-color-picker v-model="nodeAttrForm.backgroundColor" show-alpha />
</el-form-item>
<el-form-item label="是否显示边框" v-if="Object.keys(nodeAttrForm).includes('isBorder')">
<el-switch v-model="nodeAttrForm.isBorder" />
</el-form-item>
<el-form-item label="边框颜色"
v-if="Object.keys(nodeAttrForm).includes('borderColor') && nodeAttrForm.isBorder">
<el-color-picker v-model="nodeAttrForm.borderColor" 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>
<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-item label="超时时间" v-if="Object.keys(nodeAttrForm).includes('timeout')">
<el-input-number
v-model="nodeAttrForm.timeout"
:min="1000"
:step="1000"
controls-position="right"
/>
</el-form-item>
<el-form-item label="提示框" v-if="Object.keys(nodeAttrForm).includes('tooltip')">
<el-switch v-model="nodeAttrForm.tooltip" active-text="显示" inactive-text="隐藏" />
</el-form-item>
<el-form-item label="图例" v-if="Object.keys(nodeAttrForm).includes('legend')">
<el-switch v-model="nodeAttrForm.legend" active-text="显示" inactive-text="隐藏" />
</el-form-item>
<el-form-item label="饼图标签" v-if="Object.keys(nodeAttrForm).includes('label')">
<el-switch v-model="nodeAttrForm.label" active-text="显示" inactive-text="隐藏" />
</el-form-item>
<el-form-item label="轮播设置" v-if="Object.keys(nodeAttrForm).includes('swiperOptions')">
<el-button plain @click="swiperOptionsVisible = true">
配置轮播图
</el-button>
</el-form-item>
<el-form-item label="图片显示方式" v-if="Object.keys(nodeAttrForm).includes('imageFit')">
<el-select
v-model="nodeAttrForm.imageFit"
style="width: 100%"
>
<el-option
label="填充满"
value="fill"
/>
<el-option
label="固定比例"
value="contain"
/>
</el-select>
</el-form-item>
<el-form-item label="图片集" v-if="Object.keys(nodeAttrForm).includes('carouselImages')">
<el-input-tag v-model="nodeAttrForm.carouselImages" placeholder="回车确认" />
</el-form-item>
<el-form-item label="表格列设置" v-if="Object.keys(nodeAttrForm).includes('tableOptions')">
<el-button plain @click="tableOptionsVisible = true">
配置表格
</el-button>
</el-form-item>
<el-form-item label="表格动画设置" v-if="Object.keys(nodeAttrForm).includes('tableClassOption')">
<el-button plain @click="tableClassOptionsVisible = true">
配置动画
</el-button>
</el-form-item>
<el-form-item label="表头高度" v-if="Object.keys(nodeAttrForm).includes('thHeight')">
<el-input v-model="nodeAttrForm.thHeight" style="width: 100%" />
</el-form-item>
<el-form-item label="行高度" v-if="Object.keys(nodeAttrForm).includes('tdHeight')">
<el-input v-model="nodeAttrForm.tdHeight" style="width: 100%" />
</el-form-item>
<el-form-item label="表头文字颜色" v-if="Object.keys(nodeAttrForm).includes('thColor')">
<el-color-picker v-model="nodeAttrForm.thColor" show-alpha />
</el-form-item>
<el-form-item label="行文字颜色" v-if="Object.keys(nodeAttrForm).includes('tdColor')">
<el-badge value="99" class="item" :offset="[-8,0]"
v-for="(i,k) in nodeAttrForm.tdColor">
<el-color-picker style="margin-right: 4px;" v-model="nodeAttrForm.tdColor[k]" show-alpha />
<template #content>
<div class="custom-content" @click="nodeAttrForm.tdColor.splice(k, 1)">
<el-icon>
<Close />
</el-icon>
</div>
</template>
</el-badge>
<el-button :icon="Plus" @click="nodeAttrForm.tdColor.push('#fff')" />
</el-form-item>
<el-form-item label="表头背景颜色" v-if="Object.keys(nodeAttrForm).includes('thBgColor')">
<el-color-picker v-model="nodeAttrForm.thBgColor" show-alpha />
</el-form-item>
<el-form-item label="行背景颜色" v-if="Object.keys(nodeAttrForm).includes('tdBgColor')">
<el-badge value="99" class="item" :offset="[-8,0]"
v-for="(i,k) in nodeAttrForm.tdBgColor">
<el-color-picker style="margin-right: 8px;"
v-model="nodeAttrForm.tdBgColor[k]" show-alpha />
<template #content>
<div class="custom-content" @click="nodeAttrForm.tdBgColor.splice(k, 1)">
<el-icon>
<Close />
</el-icon>
</div>
</template>
</el-badge>
<el-button :icon="Plus" @click="nodeAttrForm.tdBgColor.push('#fff')" />
</el-form-item>
</el-form>
</el-collapse-item>
<el-collapse-item title="自定义配置" name="3" v-if="Object.keys(customDataForm||{}).length>0">
<el-form :model="customDataForm" label-width="auto" style="max-width: 600px">
<el-form-item label="自定义内容" v-if="Object.keys(customDataForm).includes('option')">
<el-input type="textarea" v-model="customDataForm.option" style="width: 100%" />
</el-form-item>
<el-form-item label="请求地址" v-if="Object.keys(customDataForm).includes('url')">
<el-input v-model="customDataForm.url">
<template #prepend>
<el-select v-model="customDataForm.method" style="width: 100px">
<el-option label="get" value="get" />
<el-option label="post" value="post" />
<el-option label="socket" value="socket" />
</el-select>
</template>
</el-input>
</el-form-item>
<el-form-item label="拓展代码" v-if="Object.keys(customDataForm).includes('script')">
<el-input type="textarea" v-model="customDataForm.script" style="width: 100%" />
</el-form-item>
</el-form>
</el-collapse-item>
</el-collapse>
<div v-else>
<div v-for="i in nodes.filter(e=>e.type!=='area').reverse()" class="nodeCard" @click="pitchOnNode(i)">
<div style="display: inline-block;margin-right: 8px;">
<el-dropdown trigger="click" @command="nodeOperate">
<span class="el-dropdown-link" @click.stop>
<el-icon class="settingIcon">
<Setting />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="{type:'toTop',node:i}">置顶</el-dropdown-item>
<el-dropdown-item :command="{type:'moveUp',node:i}">上移一层</el-dropdown-item>
<el-dropdown-item :command="{type:'moveDown',node:i}">下移一层</el-dropdown-item>
<el-dropdown-item :command="{type:'copy',node:i}">复制</el-dropdown-item>
<el-dropdown-item :command="{type:'del',node:i}">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<span class="cardSpan">
{{ i.name }}
</span>
</div>
</div>
</div>
</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="uploadFileUrl"
:show-file-list="false"
:limit="1"
accept=".jpg,.png"
:headers="headers"
:on-success="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>
<el-dialog v-model="tableOptionsVisible" title="表格设置" width="800">
<el-table :data="nodeAttrForm.tableOptions">
<el-table-column label="列名称" width="150">
<template #default="scope">
<el-input v-model="scope.row.name" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="数据字段" width="200">
<template #default="scope">
<el-input v-model="scope.row.field" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="列宽">
<template #default="scope">
<el-input v-model="scope.row.width" style="width: 100%" />
</template>
</el-table-column>
<el-table-column label="对齐方式">
<template #default="scope">
<el-select
v-model="scope.row.align"
placeholder="请选择"
style="width: 100%"
>
<el-option
label="左对齐"
value="left"
/>
<el-option
label="居中"
value="center"
/>
<el-option
label="右对齐"
value="right"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="类型">
<template #default="scope">
<el-select
v-model="scope.row.type"
placeholder="请选择"
style="width: 100%"
>
<el-option
label="文本"
value="文本"
/>
<el-option
label="标签"
value="标签"
/>
<el-option
label="图片"
value="图片"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button :icon="Delete" circle @click="nodeAttrForm.tableOptions.splice(scope.$index, 1)" />
</template>
</el-table-column>
</el-table>
<el-button size="small" :icon="Plus" style="width: 100%" @click="nodeAttrForm.tableOptions.push({})">添加
</el-button>
</el-dialog>
<el-dialog v-model="tableClassOptionsVisible" title="动画设置" width="500">
<el-form :model="nodeAttrForm.tableClassOption" label-width="auto">
<el-form-item label="滚动速度">
<el-input-number :controls="false" v-model="nodeAttrForm.tableClassOption.step" autocomplete="off" />
</el-form-item>
<el-form-item label="开启滚动的数据量">
<el-input-number :controls="false" v-model="nodeAttrForm.tableClassOption.limitMoveNum" autocomplete="off" />
</el-form-item>
<el-form-item label="鼠标悬停停止">
<el-switch v-model="nodeAttrForm.tableClassOption.hoverStop" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="tableClassOptionsVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="swiperOptionsVisible" title="轮播设置" width="500">
<el-form v-if="swiperOptionsVisible" :model="nodeAttrForm.swiperOptions" label-width="auto">
<el-form-item label="自动播放">
<el-switch v-model="nodeAttrForm.swiperOptions.autoplay" />
</el-form-item>
<el-form-item label="切换间隔">
<el-input-number :controls="false" v-model="nodeAttrForm.swiperOptions.delay" autocomplete="off" />
</el-form-item>
<el-form-item label="鼠标悬停停止">
<el-switch v-model="nodeAttrForm.swiperOptions.pauseOnMouseEnter" />
</el-form-item>
<el-form-item label="过渡时间">
<el-input-number :controls="false" v-model="nodeAttrForm.swiperOptions.speed" autocomplete="off" />
</el-form-item>
<el-form-item label="循环播放">
<el-switch v-model="nodeAttrForm.swiperOptions.loop" />
</el-form-item>
<el-form-item label="显示导航按钮">
<el-switch v-model="nodeAttrForm.swiperOptions.navigation" />
</el-form-item>
<el-form-item label="动画效果">
<el-select
v-model="nodeAttrForm.swiperOptions.effect"
style="width: 100%"
>
<el-option
label="默认"
value="slide"
/>
<el-option
label="淡入淡出"
value="fade"
/>
<el-option
label="立方体"
value="cube"
/>
<el-option
label="翻转"
value="flip"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="swiperOptionsVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
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';
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';
import PieNode from './nodes/board/pieNode.vue';
import NightingaleRoseDiagramNode from './nodes/board/nightingaleRoseDiagramNode.vue';
import CarouselNode from './nodes/board/carouselNode.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 IconNode from './nodes/form/iconNode.vue';
import VideoNode from './nodes/form/videoNode.vue';
import TimelineNode from './nodes/form/timelineNode.vue';
import DigitalFlopNode from './nodes/form/digitalFlopNode.vue';
import AreaNode from './nodes/other/areaNode.vue';
import TableNode from './nodes/form/tableNode.vue';
import ScrollTableNode from './nodes/form/scrollTableNode.vue';
import tool, { options } from './tool';
import { getDataSourceList } from '@/views/boardGenerate/api/dataSource';
import { editBoardApi, getBoardApi } from '@/views/boardGenerate/api/boardList';
const router = useRouter();
//
const { onDragStart, onDrop, onDragOver } = tool();
// flow
const {
addEdges,
updateNode,
removeNodes,
addNodes
} = useVueFlow();
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({});
const tableOptionsVisible = ref(false);
const tableClassOptionsVisible = ref(false);
const swiperOptionsVisible = ref(false);
const pageTitle = ref('');
const leftPanelState = ref('1');
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([]);
const customBoard = ref([]);
const boardData = ref({});
onMounted(async () => {
icons.value = Object.keys(icon);
loading.value = true
getDataSourceList({
pageNum: 1,
pageSize: 9999999
})
.then((res) => {
customData.value = res.rows.map(e => {
return {
name: e.dataSourceName,
url: e.requestUrl,
method: e.requestMethod,
outputData: e.designDataFieldList
};
});
});
customBoard.value = JSON.parse(localStorage.getItem('BOARDNODE') || '[]');
await nextTick();
getBoardApi(router.currentRoute.value.query.id)
.then((res) => {
let data = res.data;
boardData.value = data;
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) || {};
Object.keys(dataContent).forEach((key) => {
if (savaField.includes(key)) {
data[key] = dataContent[key];
} else {
if (Array.isArray(dataContent[key])) {
data[key] = [];
} else if (dataContent[key] && typeof dataContent[key] === 'object' && !Array.isArray(dataContent[key])) {
data[key] = {};
} else {
data[key] = null;
}
}
});
return {
id: e.pagePointId,
name: e.pagePointName,
dimensions: {
width: e.width,
height: e.height
},
position: {
x: e.posX,
y: e.posY
},
type: e.pointType,
data: JSON.parse(e.dataContent)
};
}) : [{
id: `area_${uuidv4().replaceAll('-', '_')}`,
name: 'area',
type: 'area',
position: {
x: 0,
y: 0
},
data: {}
}];
edges.value = data.designPageEdgesList.map(e => {
return {
id: e.pageEdgesId,
source: e.sourceNodeId,
target: e.targetNodeId,
type: e.edgeType,
targetHandle: e.targetHandleId,
sourceHandle: e.sourceHandleId
};
}) || [];
pageData.value = JSON.parse(data.customContent) || {};
})
.finally(()=>{
loading.value = false
})
});
const customDataForm = ref({});
const nodeAttrForm = ref({});
const nodeDataForm = ref({});
const logEvent = async (eventname, event) => {
switch (eventname) {
case 'connect':
if (!edges.value.some(r => r.id === `${event.source}---${event.target}`)) {
addEdges({
id: `${event.source}---${event.target}`,
source: event.source,
target: event.target,
type: 'bezier',
animated: true,
markerEnd: MarkerType.ArrowClosed,
sourceHandle: event.sourceHandle,
style: { stroke: '#409EFF' }
});
}
break;
case 'paneClick':
nodeAttrForm.value = {};
nodeDataForm.value = {};
customDataForm.value = {};
break;
case 'click':
nodeAttrForm.value = event.node.data.options;
nodeDataForm.value = event.node;
customDataForm.value = event.node.data.customData;
break;
case 'nodeDrag':
console.log(event.nodes);
if (event.nodes.length === 1) {
nodeAttrForm.value = event.node.data.options;
nodeDataForm.value = event.node;
customDataForm.value = event.node.data.customData;
} else {
nodeAttrForm.value = {};
nodeDataForm.value = {};
customDataForm.value = {};
}
break;
case 'contextmenu':
console.log('contextmenu', event);
}
};
const resize = (e, id) => {
nodes.value.forEach((item) => {
if (item.selected && item.id !== id) {
item.dimensions = {
width: e.params.width,
height: e.params.height
};
}
});
};
const getInputData = (e) => {
let outputData = {};
let nodeIds = edges.value.map(v => {
if (v.target === e) {
return v.source;
}
});
nodes.value.forEach(v => {
if (nodeIds.includes(v.id)) {
outputData = {
...outputData,
...v.data.outputData
};
}
});
return outputData;
};
const pageSetting = () => {
pageSettingVisible.value = true;
// pageSettingForm.value = JSON.parse(localStorage.getItem('PAGEDATA')) || {};
pageSettingForm.value = pageData.value;
};
const save = () => {
loading.value = true;
editBoardApi({
...boardData.value,
customContent: JSON.stringify(pageSettingForm.value),
designPagePointList: nodes.value.map((e, index) => {
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 {
activeFlag: '1',
pageConfigId: boardData.value.pageConfigId,
pagePointId: e.id,
pagePointName: e.name,
fieldOne: index,
width: e.dimensions.width,
height: e.dimensions.height,
posX: e.position.x,
posY: e.position.y,
pointType: e.type,
dataContent: JSON.stringify(data)
};
}),
designPageEdgesList: edges.value.map(e => {
console.log(e);
return {
activeFlag: '1',
pageConfigId: boardData.value.pageConfigId,
pageEdgesId: e.id,
sourceNodeId: e.source,
targetNodeId: e.target,
edgeType: e.type,
targetHandleId: e.targetHandle,
sourceHandleId: e.sourceHandle
};
})
}).then(() => {
ElMessage({
type: 'success',
message: '保存成功'
});
})
.finally(() => {
loading.value = false;
})
// 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.map(e => {
// return {
// id: e.id,
// source: e.source,
// target: e.target,
// type: e.type,
// animated: e.animated,
// markerEnd: e.markerEnd,
// targetHandle: e.targetHandle,
// sourceHandle: e.sourceHandle,
// style: e.style
// };
// })));
};
const clear = () => {
nodes.value = [{
id: `area_${uuidv4().replaceAll('-', '_')}`,
name: 'area',
type: 'area',
position: {
x: 0,
y: 0
},
data: {}
}];
edges.value = [];
};
const pageBgUploadSuccess = (res) => {
// 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;
if (res.code === 200) {
pageSettingForm.value.bg = res.data.url
}
};
const setPageData = () => {
pageData.value = JSON.parse(JSON.stringify(pageSettingForm.value));
// localStorage.setItem('PAGEDATA', JSON.stringify(pageData.value));
pageSettingVisible.value = false;
};
const asd = () => {
console.log(12312);
};
const nodeOperate = (data) => {
let type = data.type;
const index = nodes.value.indexOf(data.node);
let arr = JSON.parse(JSON.stringify(nodes.value));
const item = arr[index];
switch (type) {
case 'toTop':
nodes.value = [...nodes.value.filter(e => e.id !== data.node.id), data.node];
break;
case 'moveUp':
arr.splice(index, 1);
arr.splice(index + 1, 0, item);
nodes.value = arr;
break;
case 'moveDown':
arr.splice(index, 1);
arr.splice(index - 1, 0, item);
nodes.value = arr;
break;
case 'copy':
item.id = options.getId(item.type);
addNodes(item);
break;
case 'del':
removeNodes([data.node.id]);
break;
}
};
const pitchOnNode = (e) => {
nodes.value.forEach(v => {
if (v.id === e.id) {
v.selected = true;
} else {
v.selected = false;
}
});
nodeAttrForm.value = e.data.options;
nodeDataForm.value = e;
customDataForm.value = e.data.customData;
};
</script>
<style lang="less" scoped>
:deep( .swiper-slide-active) {
pointer-events: none;
}
.custom-content {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
:deep(.vue-flow__node-area) {
z-index: -1 !important;
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;
left: 0;
width: 300px;
height: 100%;
border-right: 1px solid #409EFF;
overflow: auto;
}
.content {
position: absolute;
top: 0;
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 {
position: absolute;
top: 0;
right: 0;
width: 300px;
height: 100%;
border-left: 1px solid #409EFF;
overflow: auto;
}
.avatar {
width: 384px;
height: 216px
}
.nodeCard {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 50px;
padding: 0 12px;
background-color: #fff;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
&:hover {
background-color: #f0f7ff;
box-shadow: 0 -4px 8px -4px rgba(0, 0, 0, 0.08),
0 4px 8px -4px rgba(0, 0, 0, 0.08);
.settingIcon {
color: #409EFF;
}
}
.cardSpan {
flex: 1;
font-size: 14px;
color: #333;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding-left: 8px;
}
.settingIcon {
font-size: 18px;
color: #888;
transition: color 0.3s ease;
}
.el-dropdown-link {
display: flex;
align-items: center;
height: 100%;
}
}
</style>
<style>
.el-notification {
display: none !important;
}
</style>