init
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "scrin-visual-editor",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"main": "dist/scrin-visual-editor.umd.js",
|
||||
"module": "dist/scrin-visual-editor.es.js",
|
||||
"style": "dist/style.css",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/scrin-visual-editor.es.js",
|
||||
"require": "./dist/scrin-visual-editor.umd.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"dev": "vite build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue-flow/background": "^1.3.2",
|
||||
"@vue-flow/core": "^1.48.2",
|
||||
"@vue-flow/node-resizer": "^1.5.1",
|
||||
"uuid": "^11.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^6.0.4",
|
||||
"element-plus": "^2.13.3",
|
||||
"less": "^4.5.1",
|
||||
"vite": "^7.3.1",
|
||||
"vue": "^3.5.29"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"element-plus": "^2.13.3",
|
||||
"vue": "^3.3.0"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 1011 B |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1000 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 955 B |
|
After Width: | Height: | Size: 527 B |
|
After Width: | Height: | Size: 527 B |
|
After Width: | Height: | Size: 527 B |
|
After Width: | Height: | Size: 632 B |
|
After Width: | Height: | Size: 632 B |
|
After Width: | Height: | Size: 456 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 635 B |
|
After Width: | Height: | Size: 532 B |
@ -0,0 +1,470 @@
|
||||
<template>
|
||||
<div class="hw-root">
|
||||
<div class="visual-editor">
|
||||
<div class="visual-editor-top">
|
||||
<div style="display: inline-block;">
|
||||
<el-popover
|
||||
:width="880"
|
||||
trigger="click"
|
||||
placement="bottom-start">
|
||||
<template #reference>
|
||||
<div style="margin-left: 20px;display: inline-block">
|
||||
<svg viewBox="0 0 1024 1024" width="20" height="20" style="margin-top: 15px;">
|
||||
<path
|
||||
d="M629.333333 160v768h-213.333333v-768h213.333333z m-277.333333 192v576h-213.333333v-576h213.333333z m554.666667 213.333333v362.666667h-213.333334v-362.666667h213.333334z m-341.333334-341.333333h-85.333333v640h85.333333v-640z m-277.333333 192h-85.333333v448h85.333333v-448z m554.666667 213.333333h-85.333334v234.666667h85.333334v-234.666667z"
|
||||
fill="#ddd" p-id="10516"></path>
|
||||
</svg>
|
||||
<el-icon color="#eee" :size="10" style="margin-top: 20px;margin-left: 8px;;vertical-align: top">
|
||||
<component :is="icon.ArrowDown"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div style="max-height: 60vh;overflow:auto;">
|
||||
<BoardNodes/>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover
|
||||
:width="620"
|
||||
trigger="click"
|
||||
placement="bottom-start">
|
||||
<template #reference>
|
||||
<div style="margin-left: 30px;display: inline-block">
|
||||
<svg viewBox="0 0 1024 1024" width="20" height="20" style="margin-top: 15px;">
|
||||
<path
|
||||
d="M108.8 224v576C108.8 902.4 313.6 960 512 960s403.2-57.6 403.2-160v-576C915.2 12.8 108.8 12.8 108.8 224z m742.4 390.4c0 32-121.6 96-339.2 96s-339.2-64-339.2-96V518.4C249.6 563.2 384 588.8 512 588.8s262.4-25.6 339.2-70.4v96z m0-300.8v108.8c0 32-121.6 96-339.2 96s-339.2-64-339.2-96V313.6h6.4c6.4 6.4 12.8 12.8 19.2 12.8h6.4c12.8 6.4 19.2 6.4 32 12.8 19.2 12.8 32 12.8 44.8 19.2 70.4 19.2 147.2 25.6 230.4 25.6s160-6.4 230.4-25.6c12.8-6.4 25.6-6.4 38.4-12.8 12.8-6.4 25.6-6.4 32-12.8h6.4c12.8-6.4 19.2-12.8 32-19.2-6.4 0 0 0 0 0zM512 128c217.6 0 339.2 64 339.2 96S729.6 320 512 320 172.8 256 172.8 224 294.4 128 512 128z m0 768c-217.6 0-339.2-64-339.2-96V704c76.8 44.8 211.2 70.4 339.2 70.4s262.4-25.6 339.2-70.4v89.6c0 38.4-121.6 102.4-339.2 102.4z"
|
||||
fill="#ddd" p-id="22021"></path>
|
||||
</svg>
|
||||
<el-icon color="#eee" :size="10" style="margin-top: 20px;margin-left: 8px;;vertical-align: top">
|
||||
<component :is="icon.ArrowDown"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div style="max-height: 60vh;overflow:auto;">
|
||||
<CustomData/>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover
|
||||
:width="620"
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #reference>
|
||||
<div style="margin-left: 30px;display: inline-block">
|
||||
<svg viewBox="0 0 1024 1024" width="20" height="20" style="margin-top: 15px;">
|
||||
<path
|
||||
d="M959.825022 384.002258V191.939717C959.825022 121.2479 902.517291 63.940169 831.825474 63.940169H191.939717C121.2479 63.940169 63.940169 121.2479 63.940169 191.939717v639.885757C63.940169 902.517291 121.2479 959.825022 191.939717 959.825022h639.885757c70.691817 0 127.999548-57.307731 127.999548-127.999548V384.002258zM146.66502 146.66502a63.737872 63.737872 0 0 1 45.336109-18.784682h639.997742A63.961844 63.961844 0 0 1 895.884854 192.001129V320.062089H127.880338V192.001129A63.737872 63.737872 0 0 1 146.66502 146.66502z m269.1267 461.308451v-223.971213h192.181751v223.971213h-192.181751z m192.181751 63.940169v223.971214h-192.181751v-223.971214h192.181751z m-256.12192-63.940169H127.880338v-223.971213h223.971213v223.971213z m-205.186531 269.235073a63.466939 63.466939 0 0 1-18.784682-45.209673V671.91364h223.971213v223.971214H192.001129a63.625887 63.625887 0 0 1-45.336109-18.67631z m749.219834-45.209673A63.763159 63.763159 0 0 1 831.998871 895.884854H671.91364v-223.971214h223.971214v160.085231z m0-224.0254h-223.971214v-223.971213h223.971214v223.971213z"
|
||||
fill="#ddd" p-id="23072"></path>
|
||||
</svg>
|
||||
<el-icon color="#eee" :size="10" style="margin-top: 20px;margin-left: 8px;;vertical-align: top">
|
||||
<component :is="icon.ArrowDown"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div style="max-height: 60vh;overflow:auto;">
|
||||
<FormNodes/>
|
||||
</div>
|
||||
</el-popover>
|
||||
<el-popover
|
||||
:width="620"
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #reference>
|
||||
<div style="margin-left: 30px;display: inline-block;vertical-align: top">
|
||||
<el-icon color="#fff" style="margin-top: 15px;">
|
||||
<component :is="icon.Opportunity"/>
|
||||
</el-icon>
|
||||
<el-icon color="#eee" :size="10" style="margin-top: 20px;margin-left: 8px;;vertical-align: top">
|
||||
<component :is="icon.ArrowDown"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div style="max-height: 60vh;overflow:auto;">
|
||||
<OtherNodes :icons="icon"/>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="options-menu">
|
||||
<el-icon color="#ddd" :size="20" style="margin-top: 15px;margin-left: 20px;vertical-align: top"
|
||||
@click="pageSetting">
|
||||
<component :is="icon.Setting"/>
|
||||
</el-icon>
|
||||
<el-icon color="#ddd" :size="20" style="margin-top: 15px;margin-left: 20px;vertical-align: top" @click="save">
|
||||
<component :is="icon.DocumentChecked"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="visual-editor-left">
|
||||
<div class="title">
|
||||
图层
|
||||
</div>
|
||||
<div class="levels">
|
||||
<el-dropdown
|
||||
style="min-width: 100%;display: inline-block"
|
||||
v-for="i in nodes.filter(e => e.type !== 'area').reverse()"
|
||||
:key="i.id"
|
||||
trigger="contextmenu"
|
||||
@command="nodeOperate">
|
||||
<div class="level" :class="{isSelect: i.selected}" @contextmenu.prevent @click="pitchOnNode(i)">
|
||||
<div class="isLock">
|
||||
<el-icon style="cursor: pointer" color="#fff" :size="16" @click="i.draggable = !i.draggable">
|
||||
<Unlock v-if="i.draggable"/>
|
||||
<Lock v-if="!i.draggable"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<svg viewBox="0 0 1024 1024" width="16" height="16" style="margin-left: 10px;">
|
||||
<path
|
||||
d="M629.333333 160v768h-213.333333v-768h213.333333z m-277.333333 192v576h-213.333333v-576h213.333333z m554.666667 213.333333v362.666667h-213.333334v-362.666667h213.333334z m-341.333334-341.333333h-85.333333v640h85.333333v-640z m-277.333333 192h-85.333333v448h85.333333v-448z m554.666667 213.333333h-85.333334v234.666667h85.333334v-234.666667z"
|
||||
fill="#ddd"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="boardName">
|
||||
<span style="display: inline-block;">{{ i.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ type: 'toTop', node: i }">置顶</el-dropdown-item>
|
||||
<el-dropdown-item :command="{ type: 'toBottom', 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>
|
||||
</div>
|
||||
<div class="visual-editor-center" @drop="onDrop" v-loading="loading">
|
||||
<FlowRuler>
|
||||
<VueFlow
|
||||
id="flowA"
|
||||
:min-zoom="0.01"
|
||||
ref="flowRef"
|
||||
:snapToGrid="isSnapToGrid"
|
||||
:snapGrid="[10, 10]"
|
||||
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="props">
|
||||
<AreaNode v-bind="props" :pageData="pageData" @resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-customBoard="props">
|
||||
<CustomBoardNode v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-customData="props">
|
||||
<CustomDataNode v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-line="props">
|
||||
<LineNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-multiLines="props">
|
||||
<MultiLinesNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-curve="props">
|
||||
<CurveNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-multiCurves="props">
|
||||
<MultiCurvesNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-lineBar="props">
|
||||
<LineBarNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-bar="props">
|
||||
<BarNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-backgroundBar="props">
|
||||
<BackgroundBarNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-multiBars="props">
|
||||
<MultiBarsNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-pie="props">
|
||||
<PieNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-annular="props">
|
||||
<AnnularNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-radar="props">
|
||||
<RadarNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-dashboard="props">
|
||||
<DashboardNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-props="props">
|
||||
<NightingaleRoseDiagramNode :colors="pageSettingForm.colors" v-bind="props"
|
||||
:inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-carousel="props">
|
||||
<CarouselNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-data="props">
|
||||
<DataNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-text="props">
|
||||
<TextNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-img="imgNodeProps">
|
||||
<ImgNode :colors="pageSettingForm.colors" v-bind="imgNodeProps" :inputData="getInputData(imgNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, imgNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-icon="iconNodeProps">
|
||||
<IconNode :colors="pageSettingForm.colors" v-bind="iconNodeProps"
|
||||
:inputData="getInputData(iconNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, iconNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-video="videoNodeProps">
|
||||
<VideoNode :colors="pageSettingForm.colors" v-bind="videoNodeProps"
|
||||
:inputData="getInputData(videoNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, videoNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-timeline="timelineNodeProps">
|
||||
<TimelineNode :colors="pageSettingForm.colors" v-bind="timelineNodeProps"
|
||||
:inputData="getInputData(timelineNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, timelineNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-digitalFlop="digitalFlopNodeProps">
|
||||
<DigitalFlopNode :colors="pageSettingForm.colors" v-bind="digitalFlopNodeProps"
|
||||
:inputData="getInputData(digitalFlopNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, digitalFlopNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-inputNode="inputNodeProps">
|
||||
<InputNode :colors="pageSettingForm.colors" v-bind="inputNodeProps"
|
||||
:inputData="getInputData(inputNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, inputNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-buttonNode="buttonNodeProps">
|
||||
<ButtonNode :colors="pageSettingForm.colors" v-bind="buttonNodeProps"
|
||||
:inputData="getInputData(buttonNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, buttonNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-selectNode="selectNodeProps">
|
||||
<SelectNode :colors="pageSettingForm.colors" v-bind="selectNodeProps"
|
||||
:inputData="getInputData(selectNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, selectNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-time="props">
|
||||
<TimeNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-map="mapNodeProps">
|
||||
<MapNode :colors="pageSettingForm.colors" v-bind="mapNodeProps" :inputData="getInputData(mapNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, mapNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-staticData="staticDataNodeProps">
|
||||
<StaticDataNode :colors="pageSettingForm.colors" v-bind="staticDataNodeProps"
|
||||
:inputData="getInputData(staticDataNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, staticDataNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-table="tableNodeProps">
|
||||
<TableNode :colors="pageSettingForm.colors" v-bind="tableNodeProps"
|
||||
:inputData="getInputData(tableNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, tableNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-scrollTable="scrollTableNodeProps">
|
||||
<ScrollTableNode :colors="pageSettingForm.colors" v-bind="scrollTableNodeProps"
|
||||
:inputData="getInputData(scrollTableNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, scrollTableNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-background="backgroundNodeProps">
|
||||
<BackgroundNode :colors="pageSettingForm.colors" v-bind="backgroundNodeProps"
|
||||
:inputData="getInputData(backgroundNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, backgroundNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-pagination="paginationNodeProps">
|
||||
<PaginationNode :colors="pageSettingForm.colors" v-bind="paginationNodeProps"
|
||||
:inputData="getInputData(paginationNodeProps.id)"
|
||||
@resize="(e) => handleResizeStop(e, paginationNodeProps)"/>
|
||||
</template>
|
||||
|
||||
<template #node-menu="props">
|
||||
<MenuNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
|
||||
<template #node-tree="props">
|
||||
<TreeNode :colors="pageSettingForm.colors" v-bind="props" :inputData="getInputData(props.id)"
|
||||
@resize="(e) => handleResizeStop(e, props)"/>
|
||||
</template>
|
||||
</VueFlow>
|
||||
</FlowRuler>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {MarkerType, useVueFlow, VueFlow} from '@vue-flow/core';
|
||||
import tool from '../../util/tool.js';
|
||||
import {Background} from '@vue-flow/background';
|
||||
import * as icon from '@element-plus/icons-vue';
|
||||
import BoardNodes from '../inside/boardNodes/index.vue'
|
||||
import CustomData from '../inside/customData/index.vue'
|
||||
import FormNodes from '../inside/formNodes/index.vue'
|
||||
import OtherNodes from '../inside/otherNodes/index.vue'
|
||||
import FlowRuler from '../inside/FlowRuler.vue'
|
||||
|
||||
const nodes = ref([])
|
||||
const {onDragStart, onDrop, onDragOver} = tool();
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.hw-root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 80vh;
|
||||
--visual-editor-top-height: 50px;
|
||||
}
|
||||
|
||||
.visual-editor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.visual-editor-top {
|
||||
width: 100%;
|
||||
height: var(--visual-editor-top-height);
|
||||
background-color: #1C1F20;
|
||||
border-bottom: 1px solid #000;
|
||||
position: relative;
|
||||
|
||||
.options-menu {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.visual-editor-left {
|
||||
width: 200px;
|
||||
height: calc(100% - var(--visual-editor-top-height));
|
||||
display: inline-block;
|
||||
overflow: auto;
|
||||
background-color: #1C1F20;
|
||||
border-right: 1px solid #000;
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
color: #FFF;
|
||||
background-color: #293133;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
.levels {
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
overflow: auto;
|
||||
background-color: #1C1F20;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.level {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
padding-left: 20px;
|
||||
border-bottom: 1px solid #000;
|
||||
|
||||
.icon, .boardName {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.boardName {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.isSelect {
|
||||
background-color: #494949;
|
||||
|
||||
.isShow {
|
||||
border-right: 1px solid #373737;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.visual-editor-center {
|
||||
width: calc(100% - 500px);
|
||||
display: inline-block;
|
||||
background-color: #1C1F20;
|
||||
height: calc(100vh - var(--visual-editor-top-height));
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div class="hw-flow-ruler-container">
|
||||
<div class="hw-flow-ruler-x">
|
||||
<canvas ref="rulerXCanvas" class="hw-flow-ruler-canvas"></canvas>
|
||||
</div>
|
||||
<div class="hw-flow-ruler-main-content">
|
||||
<div class="hw-flow-ruler-y">
|
||||
<canvas ref="rulerYCanvas" class="hw-flow-ruler-canvas"></canvas>
|
||||
</div>
|
||||
<slot/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, onMounted} from 'vue';
|
||||
import {useVueFlow} from '@vue-flow/core';
|
||||
|
||||
const rulerXCanvas = ref(null);
|
||||
const rulerYCanvas = ref(null);
|
||||
const {viewport} = useVueFlow();
|
||||
|
||||
let animationFrameId = null;
|
||||
let lastRenderTime = 0;
|
||||
const FRAME_INTERVAL = 1000 / 60;
|
||||
|
||||
const getNiceStep = (target) => {
|
||||
const steps = [1, 2, 5, 10, 20, 25, 50, 100, 200, 500, 1000];
|
||||
return steps.find(s => s * viewport.value.zoom >= target) || 1000;
|
||||
};
|
||||
|
||||
const drawRulers = () => {
|
||||
const scale = viewport.value.zoom;
|
||||
const offsetX = viewport.value.x;
|
||||
const offsetY = viewport.value.y;
|
||||
|
||||
const step = getNiceStep(10);
|
||||
const bigStep = step * 10;
|
||||
const leftOffset = 30;
|
||||
const rulerSize = 30;
|
||||
|
||||
const xCanvas = rulerXCanvas.value;
|
||||
const xCtx = xCanvas.getContext('2d');
|
||||
xCanvas.width = xCanvas.offsetWidth * window.devicePixelRatio;
|
||||
xCanvas.height = xCanvas.offsetHeight * window.devicePixelRatio;
|
||||
xCtx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
xCtx.clearRect(0, 0, xCanvas.offsetWidth, xCanvas.offsetHeight);
|
||||
|
||||
xCtx.fillStyle = '#999';
|
||||
xCtx.font = '10px sans-serif';
|
||||
|
||||
const canvasWidth = xCanvas.offsetWidth;
|
||||
const startWorldX = Math.floor((-offsetX - leftOffset) / scale / step) * step;
|
||||
const endWorldX = (-offsetX + canvasWidth) / scale;
|
||||
|
||||
for (let worldX = startWorldX; worldX < endWorldX; worldX += step) {
|
||||
const screenX = worldX * scale + offsetX + leftOffset;
|
||||
xCtx.beginPath();
|
||||
if (Math.round(worldX) % bigStep === 0) {
|
||||
xCtx.strokeStyle = '#fff';
|
||||
xCtx.moveTo(screenX, rulerSize - 20);
|
||||
xCtx.lineTo(screenX, rulerSize);
|
||||
xCtx.stroke();
|
||||
xCtx.fillStyle = '#ccc';
|
||||
xCtx.fillText(Math.round(worldX).toString(), screenX + 2, 10);
|
||||
} else {
|
||||
xCtx.strokeStyle = '#666';
|
||||
xCtx.moveTo(screenX, rulerSize - 10);
|
||||
xCtx.lineTo(screenX, rulerSize);
|
||||
xCtx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
const yCanvas = rulerYCanvas.value;
|
||||
const yCtx = yCanvas.getContext('2d');
|
||||
yCanvas.width = yCanvas.offsetWidth * window.devicePixelRatio;
|
||||
yCanvas.height = yCanvas.offsetHeight * window.devicePixelRatio;
|
||||
yCtx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
yCtx.clearRect(0, 0, yCanvas.offsetWidth, yCanvas.offsetHeight);
|
||||
|
||||
yCtx.fillStyle = '#999';
|
||||
yCtx.font = '10px sans-serif';
|
||||
|
||||
const canvasHeight = yCanvas.offsetHeight;
|
||||
const startWorldY = Math.floor((-offsetY) / scale / step) * step;
|
||||
const endWorldY = (-offsetY + canvasHeight) / scale;
|
||||
|
||||
for (let worldY = startWorldY; worldY < endWorldY; worldY += step) {
|
||||
const screenY = worldY * scale + offsetY;
|
||||
yCtx.beginPath();
|
||||
if (Math.round(worldY) % bigStep === 0) {
|
||||
yCtx.strokeStyle = '#fff';
|
||||
yCtx.save();
|
||||
yCtx.translate(rulerSize - 2, screenY + 5);
|
||||
yCtx.rotate(-Math.PI / 2);
|
||||
yCtx.fillStyle = '#ccc';
|
||||
yCtx.fillText(Math.round(worldY).toString(), 0, 0);
|
||||
yCtx.restore();
|
||||
|
||||
yCtx.moveTo(rulerSize - 20, screenY);
|
||||
yCtx.lineTo(rulerSize, screenY);
|
||||
yCtx.stroke();
|
||||
} else {
|
||||
yCtx.strokeStyle = '#666';
|
||||
yCtx.moveTo(rulerSize - 10, screenY);
|
||||
yCtx.lineTo(rulerSize, screenY);
|
||||
yCtx.stroke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderLoop = (time) => {
|
||||
if (time - lastRenderTime >= FRAME_INTERVAL) {
|
||||
drawRulers();
|
||||
lastRenderTime = time;
|
||||
}
|
||||
animationFrameId = requestAnimationFrame(renderLoop);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
animationFrameId = requestAnimationFrame(renderLoop);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hw-flow-ruler-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hw-flow-ruler-x {
|
||||
height: 30px;
|
||||
background: #1F2122;
|
||||
border-bottom: 1px solid #444;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hw-flow-ruler-y {
|
||||
width: 30px;
|
||||
background: #1F2122;
|
||||
border-right: 1px solid #444;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hw-flow-ruler-main-content {
|
||||
display: flex;
|
||||
height: calc(100% - 30px);
|
||||
}
|
||||
|
||||
.hw-flow-ruler-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div>
|
||||
<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>
|
||||
<el-image style="width: 100%; height: 100%" :src="lineImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="multiLinesImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="curveImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="multiCurvesImg" fit="contain"/>
|
||||
</template>
|
||||
<div class="moduleText">多曲线
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'lineBar')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<el-image style="width: 100%; height: 100%" :src="lineBarImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="barImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="backgroundBarImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="multiBarsImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="pieImg" fit="contain"/>
|
||||
</template>
|
||||
<div class="moduleText">饼图</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'annular')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<el-image style="width: 100%; height: 100%" :src="annularImg" fit="contain"/>
|
||||
</template>
|
||||
<div class="moduleText">中空饼图</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'dashboard')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<el-image style="width: 100%; height: 100%" :src="dashboardImg" fit="contain"/>
|
||||
</template>
|
||||
<div class="moduleText">仪表盘</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'radar')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<el-image style="width: 100%; height: 100%" :src="radarImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="nightingaleRoseDiagramImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="carouselImg" fit="contain"/>
|
||||
</template>
|
||||
<div class="moduleText">轮播图</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import backgroundBarImg from '../../../assets/images/boardNodes/backgroundBarImg.png';
|
||||
import pieImg from '../../../assets/images/boardNodes/pieImg.png';
|
||||
import multiCurvesImg from '../../../assets/images/boardNodes/multiCurvesImg.png';
|
||||
import lineBarImg from '../../../assets/images/boardNodes/lineBarImg.png';
|
||||
import multiLinesImg from '../../../assets/images/boardNodes/multiLinesImg.png';
|
||||
import multiBarsImg from '../../../assets/images/boardNodes/multiBarsImg.png';
|
||||
import annularImg from '../../../assets/images/boardNodes/annularImg.png';
|
||||
import radarImg from '../../../assets/images/boardNodes/radarImg.png';
|
||||
import carouselImg from '../../../assets/images/boardNodes/carouselImg.png';
|
||||
import nightingaleRoseDiagramImg from '../../../assets/images/boardNodes/nightingaleRoseDiagramImg.png';
|
||||
import curveImg from '../../../assets/images/boardNodes/curveImg.png';
|
||||
import dashboardImg from '../../../assets/images/boardNodes/dashboardImg.png';
|
||||
import lineImg from '../../../assets/images/boardNodes/lineImg.png';
|
||||
import barImg from '../../../assets/images/boardNodes/barImg.png';
|
||||
|
||||
defineProps({
|
||||
onDragStart: Function
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.moduleCard {
|
||||
width: 100px;
|
||||
vertical-align: top;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<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>
|
||||
<div style="width: 100%;height: 100px;text-align: center">
|
||||
|
||||
<el-image style="width:80px;height: 80px;margin-top: 10px;" :src="mapImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="customizationRequestImg" fit="contain"/>
|
||||
</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>
|
||||
<el-image style="width: 100%; height: 100%" :src="network" fit="contain"/>
|
||||
</template>
|
||||
<div class="moduleText">{{ i.name }}</div>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import customizationRequestImg from '@/assets/images/customData/customizationRequestImg.png';
|
||||
import mapImg from '@/assets/images/customData/mapImg.png';
|
||||
import network from '@/assets/images/customData/network.png';
|
||||
|
||||
defineProps({
|
||||
onDragStart: Function
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
||||
.moduleCard {
|
||||
width: 100px;
|
||||
vertical-align: top;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div>
|
||||
<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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="textImg" fit="contain"/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="moduleText">文字</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'buttonNode')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="inputNodeImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="inputNodeImg2" fit="contain"/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="moduleText">输入框</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'selectNode')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="inputNodeImg3" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="timeImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="imgImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="iconImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="videoImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="timelineImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="digitalFlopImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="scrollTableImg" fit="contain"/>
|
||||
</div>
|
||||
</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>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="scrollTableImg2" fit="contain"/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="moduleText">滚动表格</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'background')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="backgroundImg" fit="contain"/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="moduleText">背景</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'pagination')"
|
||||
:style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<div style="width: 100%;height: 100%;text-align: center">
|
||||
|
||||
<el-image style="width:50px;height: 50px;text-align: center" :src="backgroundImg" fit="contain"/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="moduleText">分页</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import backgroundImg from '@/assets/images/formNodes/backgroundImg.png';
|
||||
import digitalFlopImg from '@/assets/images/formNodes/digitalFlopImg.png';
|
||||
import iconImg from '@/assets/images/formNodes/iconImg.png';
|
||||
import imgImg from '@/assets/images/formNodes/imgImg.png';
|
||||
import inputNodeImg from '@/assets/images/formNodes/inputNodeImg.png';
|
||||
import inputNodeImg2 from '@/assets/images/formNodes/inputNodeImg2.png';
|
||||
import inputNodeImg3 from '@/assets/images/formNodes/inputNodeImg3.png';
|
||||
import scrollTableImg from '@/assets/images/formNodes/scrollTableImg.png';
|
||||
import scrollTableImg2 from '@/assets/images/formNodes/scrollTableImg2.png';
|
||||
import textImg from '@/assets/images/formNodes/textImg.png';
|
||||
import timeImg from '@/assets/images/formNodes/timeImg.png';
|
||||
import timelineImg from '@/assets/images/formNodes/timelineImg.png';
|
||||
import videoImg from '@/assets/images/formNodes/videoImg.png';
|
||||
|
||||
|
||||
defineProps({
|
||||
onDragStart: Function
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
||||
.moduleCard {
|
||||
width: 100px;
|
||||
vertical-align: top;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'menu')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<div style="width: 100%;height: 50px;line-height:50px;text-align: center">
|
||||
<el-icon>
|
||||
<component :is="icons.Menu"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<div class="moduleText">菜单</div>
|
||||
</el-card>
|
||||
<el-card class="moduleCard" shadow="never" :draggable="true"
|
||||
@dragstart="onDragStart($event, 'tree')" :style="{display:'inline-block',margin:'0 4px 4px 0'}"
|
||||
:body-style="{padding:'4px 0'}">
|
||||
<template #header>
|
||||
<div style="width: 100%;height: 50px;display: flex; justify-content: center; align-items: center;">
|
||||
<svg t="1768964556223" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="4878" width="30" height="30">
|
||||
<path
|
||||
d="M554.666667 682.666667h85.333333v128h-213.333333v-128h85.333333v-128H256v128h85.333333v128H128v-128h85.333333v-170.666667h298.666667V384h-85.333333V256h213.333333v128h-85.333333v128h298.666666v170.666667h85.333334v128h-213.333334v-128h85.333334v-128h-256v128z m42.666666-384h-128v42.666666h128V298.666667zM298.666667 725.333333H170.666667v42.666667h128v-42.666667z m597.333333 0h-128v42.666667h128v-42.666667z m-298.666667 0h-128v42.666667h128v-42.666667z"
|
||||
fill="#444444" p-id="4879"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
<div class="moduleText">树状图</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
defineProps({
|
||||
icons: Object,
|
||||
onDragStart: Function
|
||||
})
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
|
||||
|
||||
.moduleCard {
|
||||
width: 100px;
|
||||
vertical-align: top;
|
||||
|
||||
:deep(.el-card__body) {
|
||||
height: 40px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,9 @@
|
||||
import VisualEditor from './components/external/VisualEditor.vue'
|
||||
|
||||
export { VisualEditor }
|
||||
|
||||
export default {
|
||||
install(app) {
|
||||
app.component('VisualEditor', VisualEditor)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,515 @@
|
||||
import {useVueFlow} from '@vue-flow/core';
|
||||
import {ref,} from 'vue';
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
|
||||
const getId = (type) => {
|
||||
return `${type}_${uuidv4().replaceAll('-', '_')}`;
|
||||
};
|
||||
|
||||
const optionList = (e) => {
|
||||
const normalOption = {
|
||||
title: '', boundaryGap: true,
|
||||
}
|
||||
const chartOption = {
|
||||
gridTop: 30,
|
||||
gridLeft: 5,
|
||||
gridBottom: 10,
|
||||
gridRight: 10,
|
||||
xName: '',
|
||||
xNameLocation: 'end',
|
||||
xAxisLineShow: true,
|
||||
xAxisLineColor: '#DCE2E8',
|
||||
xAxisTickShow: true,
|
||||
xAxisTickInside: false,
|
||||
xAxisTickColor: '#DCE2E8',
|
||||
xAxisLabelShow: true,
|
||||
xAxisLabelInterval: true,
|
||||
xAxisLabelInside: false,
|
||||
xAxisLabelRotate: 0,
|
||||
xAxisLabelFormatter: '',
|
||||
xAxisLabelColor: '#fff',
|
||||
xAxisLabelFontSize: 12,
|
||||
xAxisLabelMargin: 3,
|
||||
xAxisSplitLineShow: false,
|
||||
xAxisSplitLineColor: '#DCE2E8',
|
||||
xAxisSplitLineType: 'solid',
|
||||
yName: '',
|
||||
yNames: [],
|
||||
yNameLocation: 'end',
|
||||
yAxisLineShow: true,
|
||||
yAxisLineColor: '#DCE2E8',
|
||||
yAxisTickShow: true,
|
||||
yAxisTickInside: false,
|
||||
yAxisTickColor: '#DCE2E8',
|
||||
yAxisLabelShow: true,
|
||||
yAxisLabelInterval: true,
|
||||
yAxisLabelInside: false,
|
||||
yAxisLabelRotate: 0,
|
||||
yAxisLabelFormatter: '',
|
||||
yAxisLabelColor: '#fff',
|
||||
yAxisLabelFontSize: 12,
|
||||
yAxisLabelMargin: 3,
|
||||
yAxisSplitLineShow: false,
|
||||
yAxisSplitLineColor: '#DCE2E8',
|
||||
yAxisSplitLineType: 'solid',
|
||||
lineSymbolShow: false,
|
||||
lineSymbolSize: 5,
|
||||
lineSymbolType: 'circle',
|
||||
seriesColor: '#9E87FF',
|
||||
tooltip: true,
|
||||
legend: true,
|
||||
}
|
||||
const options = {
|
||||
...normalOption, ...chartOption,
|
||||
}
|
||||
return e.map(v => {
|
||||
return {[v]: options[v]};
|
||||
})
|
||||
}
|
||||
|
||||
const getOption = (e) => {
|
||||
let res = {}
|
||||
if (e === 'line' || e === 'multiLines') {
|
||||
return optionList(['title', 'boundaryGap', 'gridTop', 'gridLeft', 'gridBottom', 'gridRight', 'xName', 'xNameLocation', 'xAxisLineShow', 'xAxisLineColor', 'xAxisTickShow', 'xAxisTickInside', 'xAxisTickColor', 'xAxisLabelShow', 'xAxisLabelInterval', 'xAxisLabelInside', 'xAxisLabelRotate', 'xAxisLabelFormatter', 'xAxisLabelColor', 'xAxisLabelFontSize', 'xAxisLabelMargin', 'xAxisSplitLineShow', 'xAxisSplitLineColor', 'xAxisSplitLineType', 'yName', 'yNames', 'yNameLocation', 'yAxisLineShow', 'yAxisLineColor', 'yAxisTickShow', 'yAxisTickInside', 'yAxisTickColor', 'yAxisLabelShow', 'yAxisLabelInterval', 'yAxisLabelInside', 'yAxisLabelRotate', 'yAxisLabelFormatter', 'yAxisLabelColor', 'yAxisLabelFontSize', 'yAxisLabelMargin', 'yAxisSplitLineShow', 'yAxisSplitLineColor', 'yAxisSplitLineType', 'lineSymbolShow', 'lineSymbolSize', 'lineSymbolType', 'seriesColor', 'tooltip', 'legend'])
|
||||
} else if (e === 'bar' || e === 'multiBars') {
|
||||
return {
|
||||
title: '',
|
||||
yNames: [],
|
||||
gridTop: 30,
|
||||
gridLeft: 5,
|
||||
gridBottom: 10,
|
||||
gridRight: 10,
|
||||
xName: '',
|
||||
xNameLocation: 'end',
|
||||
xAxisLineShow: true,
|
||||
xAxisLineColor: '#DCE2E8',
|
||||
xAxisTickShow: true,
|
||||
xAxisTickInside: false,
|
||||
xAxisTickColor: '#DCE2E8',
|
||||
xAxisLabelShow: true,
|
||||
xAxisLabelInterval: true,
|
||||
xAxisLabelInside: false,
|
||||
xAxisLabelRotate: 0,
|
||||
xAxisLabelFormatter: '',
|
||||
xAxisLabelColor: '#fff',
|
||||
xAxisLabelFontSize: 12,
|
||||
xAxisLabelMargin: 3,
|
||||
xAxisSplitLineShow: false,
|
||||
xAxisSplitLineColor: '#DCE2E8',
|
||||
xAxisSplitLineType: 'solid',
|
||||
yName: '',
|
||||
yNameLocation: 'end',
|
||||
yAxisLineShow: true,
|
||||
yAxisLineColor: '#DCE2E8',
|
||||
yAxisTickShow: true,
|
||||
yAxisTickInside: false,
|
||||
yAxisTickColor: '#DCE2E8',
|
||||
yAxisLabelShow: true,
|
||||
yAxisLabelInterval: true,
|
||||
yAxisLabelInside: false,
|
||||
yAxisLabelRotate: 0,
|
||||
yAxisLabelFormatter: '',
|
||||
yAxisLabelColor: '#fff',
|
||||
yAxisLabelFontSize: 12,
|
||||
yAxisLabelMargin: 3,
|
||||
yAxisSplitLineShow: false,
|
||||
yAxisSplitLineColor: '#DCE2E8',
|
||||
yAxisSplitLineType: 'solid',
|
||||
seriesColor: '#9E87FF',
|
||||
tooltip: true,
|
||||
legend: true,
|
||||
isHorizontal: false,
|
||||
};
|
||||
} else if (e === 'backgroundBar') {
|
||||
return {
|
||||
title: '',
|
||||
yNames: [],
|
||||
gridTop: 30,
|
||||
gridLeft: 5,
|
||||
gridBottom: 10,
|
||||
gridRight: 10,
|
||||
xName: '',
|
||||
xNameLocation: 'end',
|
||||
xAxisLineShow: true,
|
||||
xAxisLineColor: '#DCE2E8',
|
||||
xAxisTickShow: true,
|
||||
xAxisTickInside: false,
|
||||
xAxisTickColor: '#DCE2E8',
|
||||
xAxisLabelShow: true,
|
||||
xAxisLabelInterval: true,
|
||||
xAxisLabelInside: false,
|
||||
xAxisLabelRotate: 0,
|
||||
xAxisLabelFormatter: '',
|
||||
xAxisLabelColor: '#fff',
|
||||
xAxisLabelFontSize: 12,
|
||||
xAxisLabelMargin: 3,
|
||||
xAxisSplitLineShow: false,
|
||||
xAxisSplitLineColor: '#DCE2E8',
|
||||
xAxisSplitLineType: 'solid',
|
||||
yName: '',
|
||||
yNameLocation: 'end',
|
||||
yAxisLineShow: true,
|
||||
yAxisLineColor: '#DCE2E8',
|
||||
yAxisTickShow: true,
|
||||
yAxisTickInside: false,
|
||||
yAxisTickColor: '#DCE2E8',
|
||||
yAxisLabelShow: true,
|
||||
yAxisLabelInterval: true,
|
||||
yAxisLabelInside: false,
|
||||
yAxisLabelRotate: 0,
|
||||
yAxisLabelFormatter: '',
|
||||
yAxisLabelColor: '#fff',
|
||||
yAxisLabelFontSize: 12,
|
||||
yAxisLabelMargin: 3,
|
||||
yAxisSplitLineShow: false,
|
||||
yAxisSplitLineColor: '#DCE2E8',
|
||||
yAxisSplitLineType: 'solid',
|
||||
tooltip: true,
|
||||
legend: true,
|
||||
seriesColor: '#9E87FF',
|
||||
backgroundColor: 'rgba(180, 180, 180, 0.2)',
|
||||
isHorizontal: false,
|
||||
};
|
||||
} else if (e === 'curve' || e === 'multiCurves') {
|
||||
return {
|
||||
title: '',
|
||||
yNames: [],
|
||||
gridTop: 30,
|
||||
gridLeft: 5,
|
||||
gridBottom: 10,
|
||||
gridRight: 10,
|
||||
boundaryGap: true,
|
||||
xName: '',
|
||||
xNameLocation: 'end',
|
||||
xAxisLineShow: true,
|
||||
xAxisLineColor: '#DCE2E8',
|
||||
xAxisTickShow: true,
|
||||
xAxisTickInside: false,
|
||||
xAxisTickColor: '#DCE2E8',
|
||||
xAxisLabelShow: true,
|
||||
xAxisLabelInterval: true,
|
||||
xAxisLabelInside: false,
|
||||
xAxisLabelRotate: 0,
|
||||
xAxisLabelFormatter: '',
|
||||
xAxisLabelColor: '#fff',
|
||||
xAxisLabelFontSize: 12,
|
||||
xAxisLabelMargin: 3,
|
||||
xAxisSplitLineShow: false,
|
||||
xAxisSplitLineColor: '#DCE2E8',
|
||||
xAxisSplitLineType: 'solid',
|
||||
yName: '',
|
||||
yNameLocation: 'end',
|
||||
yAxisLineShow: true,
|
||||
yAxisLineColor: '#DCE2E8',
|
||||
yAxisTickShow: true,
|
||||
yAxisTickInside: false,
|
||||
yAxisTickColor: '#DCE2E8',
|
||||
yAxisLabelShow: true,
|
||||
yAxisLabelInterval: true,
|
||||
yAxisLabelInside: false,
|
||||
yAxisLabelRotate: 0,
|
||||
yAxisLabelFormatter: '',
|
||||
yAxisLabelColor: '#fff',
|
||||
yAxisLabelFontSize: 12,
|
||||
yAxisLabelMargin: 3,
|
||||
yAxisSplitLineShow: false,
|
||||
yAxisSplitLineColor: '#DCE2E8',
|
||||
yAxisSplitLineType: 'solid',
|
||||
lineSymbolShow: false,
|
||||
lineSymbolSize: 5,
|
||||
lineSymbolType: 'circle',
|
||||
seriesColor: '#9E87FF',
|
||||
tooltip: true,
|
||||
legend: true
|
||||
};
|
||||
} else if (e === 'lineBar') {
|
||||
return {
|
||||
title: '',
|
||||
yNames: [],
|
||||
gridTop: 30,
|
||||
gridLeft: 5,
|
||||
gridBottom: 10,
|
||||
gridRight: 10,
|
||||
xName: '',
|
||||
xNameLocation: 'end',
|
||||
xAxisLineShow: true,
|
||||
xAxisLineColor: '#DCE2E8',
|
||||
xAxisTickShow: true,
|
||||
xAxisTickInside: false,
|
||||
xAxisTickColor: '#DCE2E8',
|
||||
xAxisLabelShow: true,
|
||||
xAxisLabelInterval: true,
|
||||
xAxisLabelInside: false,
|
||||
xAxisLabelRotate: 0,
|
||||
xAxisLabelFormatter: '',
|
||||
xAxisLabelColor: '#fff',
|
||||
xAxisLabelFontSize: 12,
|
||||
xAxisLabelMargin: 3,
|
||||
xAxisSplitLineShow: false,
|
||||
xAxisSplitLineColor: '#DCE2E8',
|
||||
xAxisSplitLineType: 'solid',
|
||||
yName: '',
|
||||
yNameLocation: 'end',
|
||||
yAxisLineShow: true,
|
||||
yAxisLineColor: '#DCE2E8',
|
||||
yAxisTickShow: true,
|
||||
yAxisTickInside: false,
|
||||
yAxisTickColor: '#DCE2E8',
|
||||
yAxisLabelShow: true,
|
||||
yAxisLabelInterval: true,
|
||||
yAxisLabelInside: false,
|
||||
yAxisLabelRotate: 0,
|
||||
yAxisLabelFormatter: '',
|
||||
yAxisLabelColor: '#fff',
|
||||
yAxisLabelFontSize: 12,
|
||||
yAxisLabelMargin: 3,
|
||||
yAxisSplitLineShow: false,
|
||||
yAxisSplitLineColor: '#DCE2E8',
|
||||
yAxisSplitLineType: 'solid',
|
||||
lineSymbolShow: false,
|
||||
lineSymbolSize: 5,
|
||||
lineSymbolType: 'circle',
|
||||
tooltip: true,
|
||||
legend: true,
|
||||
seriesColor: '#9E87FF'
|
||||
};
|
||||
} else if (e === 'radar') {
|
||||
return {
|
||||
title: '',
|
||||
seriesColor: '#9E87FF',
|
||||
yNames: [],
|
||||
tooltip: true,
|
||||
legend: true,
|
||||
radarCenter: [30, 50],
|
||||
radarRadius: 40,
|
||||
radarShape: 'polygon',
|
||||
radarSplitNumber: 5,
|
||||
splitLineColor: '#9E87FF'
|
||||
};
|
||||
} else if (e === 'pie' || e === 'nightingaleRoseDiagram') {
|
||||
return {
|
||||
title: '',
|
||||
yNames: [],
|
||||
tooltip: true,
|
||||
legend: true,
|
||||
label: true,
|
||||
pieCenter: 50,
|
||||
pieLabelFormatter: '',
|
||||
legendColor: '#fff'
|
||||
};
|
||||
} else if (e === 'annular') {
|
||||
return {
|
||||
title: '',
|
||||
yNames: [],
|
||||
tooltip: true,
|
||||
legend: true,
|
||||
label: true,
|
||||
pieCenter: 50,
|
||||
pieRadius: [30, 70],
|
||||
colorList: ['#D6F4FF', '#D6F4FF', '#D6F4FF', '#D6F4FF'],
|
||||
};
|
||||
} else if (e === 'dashboard') {
|
||||
return {title: '', yNames: [], tooltip: true, legend: true, label: true};
|
||||
} else if (e === 'customBoard') {
|
||||
return {title: '', yNames: []};
|
||||
} else if (e === 'data') {
|
||||
return {timeout: 5};
|
||||
} else if (e === 'staticData') {
|
||||
return {defaultInputArea: '', field: 'input', defaultDataType: ''};
|
||||
} else if (e === 'customData') {
|
||||
return {timeout: 5};
|
||||
} else if (e === 'map') {
|
||||
return {dataMap: []};
|
||||
} else if (e === 'inputNode') {
|
||||
return {field: 'input', defaultInput: ''};
|
||||
} else if (e === 'buttonNode') {
|
||||
return {backgroundColor: '#409EFF', color: '#fff', text: '按钮', sendType: ['ok']};
|
||||
} else if (e === 'selectNode') {
|
||||
return {field: 'select', defaultInput: '', labelField: 'label', valueField: 'value'};
|
||||
} else if (e === 'time') {
|
||||
return {startTimeId: 'startTime', endTimeId: 'endTime', defaultTime: [], format: ''};
|
||||
} else if (e === 'text') {
|
||||
return {text: '文字', align: '', color: '#fff', whiteSpace: 'nowrap'};
|
||||
} else if (e === 'img') {
|
||||
return {imgSrc: ''};
|
||||
} else if (e === 'icon') {
|
||||
return {icon: 'Tools', iconSrc: ''};
|
||||
} else if (e === 'video') {
|
||||
return {videoSrc: ''};
|
||||
} else if (e === 'timeline') {
|
||||
return {
|
||||
color: '#fff', timestampColor: '#fff', field: 'content', timestampField: 'timestamp', isTimestamp: true
|
||||
};
|
||||
} else if (e === 'scrollTable' || e === 'table') {
|
||||
return {
|
||||
tableOptions: [],
|
||||
tableCellOptions: [],
|
||||
tableClassOption: {},
|
||||
isThShow: true,
|
||||
thHeight: '32px',
|
||||
tdHeight: '28px',
|
||||
thColor: '#D6F4FF',
|
||||
tdColor: ['#D6F4FF', '#D6F4FF', '#D6F4FF', '#D6F4FF'],
|
||||
thBgColor: '#1E90FF',
|
||||
tdBgColor: ['#0D2B3D', '#103B4C', '#14576B', '#187E99']
|
||||
};
|
||||
} else if (e === 'customTable') {
|
||||
return {
|
||||
tableOptions: [],
|
||||
tableCellOptions: [],
|
||||
cellNum: 2,
|
||||
dataNum: 2,
|
||||
tdHeight: '28px',
|
||||
tdColor: ['#D6F4FF', '#D6F4FF', '#D6F4FF', '#D6F4FF'],
|
||||
tdBgColor: ['#0D2B3D', '#103B4C', '#14576B', '#187E99']
|
||||
};
|
||||
} else if (e === 'carousel') {
|
||||
return {swiperOptions: {}, imageFit: 'contain', carouselImages: []};
|
||||
} else if (e === 'background') {
|
||||
return {backgroundColor: '#fff', isBorder: true, borderColor: '#fff', backgroundImage: ''};
|
||||
} else if (e === 'pagination') {
|
||||
return {pageSizeField: 'pageSize', currentPageField: 'pageNum'};
|
||||
} else if (e === 'digitalFlop') {
|
||||
return {
|
||||
field: '',
|
||||
color: '#fff',
|
||||
fontSize: 22,
|
||||
number: 123,
|
||||
numQuantity: 3,
|
||||
backgroundColor: 'rgba(180, 180, 180, 0.2)',
|
||||
isBorder: true,
|
||||
borderColor: '#fff'
|
||||
};
|
||||
} else if (e === 'menu') {
|
||||
return {menuField: '', activeField: '', field: '', color: '#fff', activeColor: '#ffd04b'};
|
||||
} else if (e === 'tree') {
|
||||
return {level: 2, treeOptions: []};
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
const getNodeSize = (e) => {
|
||||
if (e === 'line' || e === 'multiLines' || e === 'bar' || e === 'multiBars' || e === 'curve' || e === 'multiCurves' || e === 'customBoard' || e === 'pie' || e === 'annular' || e === 'dashboard' || e === 'radar') {
|
||||
return {width: 300, height: 150};
|
||||
} else if (e === 'data' || e === 'customData') {
|
||||
return {width: 150, height: 50};
|
||||
} else if (e === 'inputNode') {
|
||||
return {width: 100, height: 30};
|
||||
} else if (e === 'buttonNode') {
|
||||
return {width: 100, height: 30};
|
||||
} else if (e === 'staticData') {
|
||||
return {width: 100, height: 60};
|
||||
} else if (e === 'text') {
|
||||
return {width: 100, height: 30};
|
||||
} else if (e === 'time') {
|
||||
return {width: 200, height: 30};
|
||||
} else if (e === 'img') {
|
||||
return {width: 300, height: 300};
|
||||
} else if (e === 'map') {
|
||||
return {width: 100, height: 30};
|
||||
} else if (e === 'menu') {
|
||||
return {width: 200, height: 800};
|
||||
} else if (e === 'scrollTable' || e === 'table') {
|
||||
return {width: 500, height: 300};
|
||||
} else if (e === 'tree') {
|
||||
return {width: 800, height: 800};
|
||||
} else {
|
||||
return {width: 100, height: 100};
|
||||
}
|
||||
};
|
||||
const nameEnum = {
|
||||
line: '折线',
|
||||
multiLines: '多折线',
|
||||
curve: '曲线',
|
||||
multiCurves: '多曲线',
|
||||
lineBar: '双轴图',
|
||||
bar: '柱状图',
|
||||
backgroundBar: '背景柱状图',
|
||||
multiBars: '多柱状图',
|
||||
pie: '饼图',
|
||||
annular: '中空饼图',
|
||||
dashboard: '仪表盘',
|
||||
radar: '雷达图',
|
||||
nightingaleRoseDiagram: '南丁格尔玫瑰图',
|
||||
carousel: '轮播图',
|
||||
map: '映射',
|
||||
staticData: '静态数据',
|
||||
customData: '自定义数据',
|
||||
text: '文字',
|
||||
inputNode: '输入框',
|
||||
time: '时间',
|
||||
img: '图片',
|
||||
icon: '图标',
|
||||
video: '视频',
|
||||
timeline: '时间线',
|
||||
digitalFlop: '数字翻牌器',
|
||||
scrollTable: '滚动表格',
|
||||
background: '背景',
|
||||
tree: '树状图'
|
||||
};
|
||||
const tool = () => {
|
||||
const nodeType = ref('');
|
||||
const customData = ref({script: ''});
|
||||
const {addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode} = useVueFlow({id: 'flowA'});
|
||||
const onDragStart = (event, type, data) => {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.setData('application/vueflow', type);
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
}
|
||||
|
||||
nodeType.value = type;
|
||||
customData.value = {...customData.value, ...(data || {})};
|
||||
document.addEventListener('drop', onDragEnd);
|
||||
};
|
||||
const onDragEnd = () => {
|
||||
nodeType.value = null;
|
||||
document.removeEventListener('drop', onDragEnd);
|
||||
};
|
||||
const onDrop = (event) => {
|
||||
const dimensions = getNodeSize(nodeType.value);
|
||||
|
||||
const position = screenToFlowCoordinate({
|
||||
x: event.clientX, y: event.clientY
|
||||
});
|
||||
|
||||
const nodeId = getId(nodeType.value);
|
||||
const newNode = {
|
||||
id: nodeId,
|
||||
name: nameEnum[nodeType.value],
|
||||
draggable: true,
|
||||
type: nodeType.value,
|
||||
dimensions,
|
||||
position,
|
||||
data: {options: getOption(nodeType.value), outputData: {}, customData: customData.value}
|
||||
};
|
||||
|
||||
|
||||
const {off} = onNodesInitialized(() => {
|
||||
updateNode(nodeId, (node) => ({
|
||||
position: {
|
||||
x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2
|
||||
}
|
||||
}));
|
||||
|
||||
off();
|
||||
});
|
||||
|
||||
addNodes(newNode);
|
||||
|
||||
};
|
||||
const onDragOver = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (nodeType.value) {
|
||||
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
onDragStart, onDragEnd, onDrop, onDragOver
|
||||
};
|
||||
};
|
||||
export default tool;
|
||||
@ -0,0 +1,38 @@
|
||||
import {defineConfig} from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import path from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
// @ 指向 src 目录
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, 'src/index.js'),
|
||||
name: 'ScrinVisualEditor',
|
||||
fileName: (format) => {
|
||||
if (format === 'es') return 'scrin-visual-editor.es.js'
|
||||
if (format === 'umd') return 'scrin-visual-editor.umd.js'
|
||||
return `scrin-visual-editor.${format}.js`
|
||||
},
|
||||
cssFileName: 'style'
|
||||
},
|
||||
preserveModules: false,
|
||||
assetsInlineLimit: 0,
|
||||
output: {
|
||||
preserveModulesRoot: 'src',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['vue'],
|
||||
output: {
|
||||
globals: {
|
||||
vue: 'Vue'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||