feat: 添加画布一件美化
This commit is contained in:
parent
2722b5a406
commit
7369231690
@ -17,6 +17,7 @@
|
|||||||
"@ctrl/tinycolor": "^4.1.0",
|
"@ctrl/tinycolor": "^4.1.0",
|
||||||
"@logicflow/core": "^1.2.27",
|
"@logicflow/core": "^1.2.27",
|
||||||
"@logicflow/extension": "^1.2.27",
|
"@logicflow/extension": "^1.2.27",
|
||||||
|
"@antv/layout": "^0.3.1",
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@wecom/jssdk": "^2.1.0",
|
"@wecom/jssdk": "^2.1.0",
|
||||||
"axios": "^0.28.0",
|
"axios": "^0.28.0",
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const props = defineProps<{
|
|||||||
modelValue?: Array<any>
|
modelValue?: Array<any>
|
||||||
}>()
|
}>()
|
||||||
const rowTemp = ref<any>()
|
const rowTemp = ref<any>()
|
||||||
const evalF = (text: string, row: any) => {
|
const evalF: (text: string, row: any) => string = (text: string, row: any) => {
|
||||||
rowTemp.value = row
|
rowTemp.value = row
|
||||||
return eval(text)
|
return eval(text)
|
||||||
}
|
}
|
||||||
@ -167,8 +167,8 @@ watch(
|
|||||||
|
|
||||||
const activeText = computed(() => {
|
const activeText = computed(() => {
|
||||||
if (props.modelValue) {
|
if (props.modelValue) {
|
||||||
const rows = option_list.value.filter(
|
const rows = option_list.value.filter((f: any) =>
|
||||||
(f: any) => props.modelValue?.includes(f[valueField.value])
|
props.modelValue?.includes(f[valueField.value])
|
||||||
)
|
)
|
||||||
if (rows) {
|
if (rows) {
|
||||||
if (rows.length > 3) {
|
if (rows.length > 3) {
|
||||||
|
|||||||
@ -1228,6 +1228,28 @@ export const iconMap: any = {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'app-beautify': {
|
||||||
|
iconReader: () => {
|
||||||
|
return h('i', [
|
||||||
|
h(
|
||||||
|
'svg',
|
||||||
|
{
|
||||||
|
style: { height: '100%', width: '100%' },
|
||||||
|
viewBox: '0 0 1024 1024',
|
||||||
|
version: '1.1',
|
||||||
|
xmlns: 'http://www.w3.org/2000/svg'
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h('path', {
|
||||||
|
d: 'M739.6864 689.92l4.2496 3.584 136.4992 135.936a34.1504 34.1504 0 0 1-43.9296 51.968l-4.1984-3.584-136.5504-135.936a34.1504 34.1504 0 0 1 43.9296-51.968zM663.4496 151.552a34.1504 34.1504 0 0 1 51.2512 30.464l-5.9392 216.6272 156.4672 146.1248a34.1504 34.1504 0 0 1-8.6528 55.808l-4.8128 1.792-202.8032 61.0816-87.4496 197.12a34.1504 34.1504 0 0 1-56.32 9.216l-3.2768-4.096-119.5008-178.432-209.9712-24.064a34.1504 34.1504 0 0 1-26.1632-50.176l2.7648-4.3008 129.28-171.7248-42.5472-212.3776a34.1504 34.1504 0 0 1 40.448-40.1408l4.6592 1.3312 198.912 72.3456z m-18.6368 89.7536l-144.5376 83.968a34.1504 34.1504 0 0 1-28.8256 2.56L314.5728 270.592l33.792 167.8848c1.4848 7.68 0.3584 15.5136-3.1744 22.3232l-3.072 4.9152-102.656 136.2944 166.4 19.1488c8.2944 0.9216 15.872 4.864 21.4016 10.9568l3.072 3.9424 93.8496 140.032 68.7104-154.7776a34.1504 34.1504 0 0 1 16.7936-17.0496l4.608-1.792 160.9216-48.4864-124.2624-116.0192a34.1504 34.1504 0 0 1-10.4448-20.0704l-0.3584-5.7856 4.6592-170.9056z',
|
||||||
|
fill: 'currentColor'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
// 'app-history-outlined': {
|
// 'app-history-outlined': {
|
||||||
// iconReader: () => {
|
// iconReader: () => {
|
||||||
// return h('i', [
|
// return h('i', [
|
||||||
|
|||||||
@ -7,12 +7,7 @@
|
|||||||
import { onMounted, nextTick, watch, onBeforeUnmount, ref } from 'vue'
|
import { onMounted, nextTick, watch, onBeforeUnmount, ref } from 'vue'
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
const tmp = ref()
|
const tmp = ref()
|
||||||
const props = defineProps({
|
const props = defineProps<{ option: string }>()
|
||||||
option: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const chartsRef = ref()
|
const chartsRef = ref()
|
||||||
|
|
||||||
const style = ref({
|
const style = ref({
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { computed, ref } from 'vue'
|
|||||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
form_setting: string
|
form_setting: string
|
||||||
sendMessage: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||||
}>()
|
}>()
|
||||||
const form_setting_data = computed(() => {
|
const form_setting_data = computed(() => {
|
||||||
if (props.form_setting) {
|
if (props.form_setting) {
|
||||||
@ -68,11 +68,13 @@ const submit = () => {
|
|||||||
dynamicsFormRef.value?.validate().then(() => {
|
dynamicsFormRef.value?.validate().then(() => {
|
||||||
_submit.value = true
|
_submit.value = true
|
||||||
const setting = JSON.parse(props.form_setting)
|
const setting = JSON.parse(props.form_setting)
|
||||||
props.sendMessage('', 'old', {
|
if (props.sendMessage) {
|
||||||
runtime_node_id: setting.runtime_node_id,
|
props.sendMessage('', 'old', {
|
||||||
chat_record_id: setting.chat_record_id,
|
runtime_node_id: setting.runtime_node_id,
|
||||||
node_data: form_data.value
|
chat_record_id: setting.chat_record_id,
|
||||||
})
|
node_data: form_data.value
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
:option="item.content"
|
:option="item.content"
|
||||||
></EchartsRander>
|
></EchartsRander>
|
||||||
<FormRander
|
<FormRander
|
||||||
:sendMessage="sendMessage"
|
:send-message="sendMessage"
|
||||||
v-else-if="item.type === 'form_rander'"
|
v-else-if="item.type === 'form_rander'"
|
||||||
:form_setting="item.content"
|
:form_setting="item.content"
|
||||||
></FormRander>
|
></FormRander>
|
||||||
|
|||||||
@ -11,6 +11,9 @@
|
|||||||
<el-button link @click="fitView">
|
<el-button link @click="fitView">
|
||||||
<AppIcon iconName="app-fitview" title="适应"></AppIcon>
|
<AppIcon iconName="app-fitview" title="适应"></AppIcon>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button link @click="layout">
|
||||||
|
<AppIcon iconName="app-beautify" title="美化"></AppIcon>
|
||||||
|
</el-button>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -30,5 +33,8 @@ function fitView() {
|
|||||||
props.lf?.resetTranslate()
|
props.lf?.resetTranslate()
|
||||||
props.lf?.fitView()
|
props.lf?.fitView()
|
||||||
}
|
}
|
||||||
|
const layout = () => {
|
||||||
|
props.lf?.extension.dagre.layout()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@ -137,6 +137,26 @@ class AppNode extends HtmlResize.view {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AppNodeModel extends HtmlResize.model {
|
class AppNodeModel extends HtmlResize.model {
|
||||||
|
refreshDeges() {
|
||||||
|
// 更新节点连接边的path
|
||||||
|
this.incoming.edges.forEach((edge: any) => {
|
||||||
|
// 调用自定义的更新方案
|
||||||
|
edge.updatePathByAnchor()
|
||||||
|
})
|
||||||
|
this.outgoing.edges.forEach((edge: any) => {
|
||||||
|
edge.updatePathByAnchor()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
set_position(position: { x?: number; y?: number }) {
|
||||||
|
const { x, y } = position
|
||||||
|
if (x) {
|
||||||
|
this.x = x
|
||||||
|
}
|
||||||
|
if (y) {
|
||||||
|
this.y = y
|
||||||
|
}
|
||||||
|
this.refreshDeges()
|
||||||
|
}
|
||||||
getResizeOutlineStyle() {
|
getResizeOutlineStyle() {
|
||||||
const style = super.getResizeOutlineStyle()
|
const style = super.getResizeOutlineStyle()
|
||||||
style.stroke = 'none'
|
style.stroke = 'none'
|
||||||
|
|||||||
@ -5,13 +5,14 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LogicFlow from '@logicflow/core'
|
import LogicFlow from '@logicflow/core'
|
||||||
import { ref, onMounted, computed } from 'vue'
|
import { ref, onMounted, computed, nextTick } from 'vue'
|
||||||
import AppEdge from './common/edge'
|
import AppEdge from './common/edge'
|
||||||
import Control from './common/NodeControl.vue'
|
import Control from './common/NodeControl.vue'
|
||||||
import { baseNodes } from '@/workflow/common/data'
|
import { baseNodes } from '@/workflow/common/data'
|
||||||
import '@logicflow/extension/lib/style/index.css'
|
import '@logicflow/extension/lib/style/index.css'
|
||||||
import '@logicflow/core/dist/style/index.css'
|
import '@logicflow/core/dist/style/index.css'
|
||||||
import { initDefaultShortcut } from '@/workflow/common/shortcut'
|
import { initDefaultShortcut } from '@/workflow/common/shortcut'
|
||||||
|
import Dagre from '@/workflow/plugins/dagre'
|
||||||
const nodes: any = import.meta.glob('./nodes/**/index.ts', { eager: true })
|
const nodes: any = import.meta.glob('./nodes/**/index.ts', { eager: true })
|
||||||
|
|
||||||
defineOptions({ name: 'WorkFlow' })
|
defineOptions({ name: 'WorkFlow' })
|
||||||
@ -61,6 +62,7 @@ const renderGraphData = (data?: any) => {
|
|||||||
const container: any = document.querySelector('#container')
|
const container: any = document.querySelector('#container')
|
||||||
if (container) {
|
if (container) {
|
||||||
lf.value = new LogicFlow({
|
lf.value = new LogicFlow({
|
||||||
|
plugins: [Dagre],
|
||||||
textEdit: false,
|
textEdit: false,
|
||||||
adjustEdge: false,
|
adjustEdge: false,
|
||||||
adjustEdgeStartAndEnd: false,
|
adjustEdgeStartAndEnd: false,
|
||||||
|
|||||||
69
ui/src/workflow/plugins/dagre.ts
Normal file
69
ui/src/workflow/plugins/dagre.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { DagreLayout, type DagreLayoutOptions } from '@antv/layout'
|
||||||
|
|
||||||
|
export default class Dagre {
|
||||||
|
static pluginName = 'dagre'
|
||||||
|
lf: any
|
||||||
|
option: DagreLayoutOptions | any
|
||||||
|
render(lf: any) {
|
||||||
|
this.lf = lf
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* option: {
|
||||||
|
* rankdir: "TB", // layout 方向, 可选 TB, BT, LR, RL
|
||||||
|
* align: undefined, // 节点对齐方式,可选 UL, UR, DL, DR
|
||||||
|
* nodeSize: undefined, // 节点大小
|
||||||
|
* nodesepFunc: undefined, // 节点水平间距(px)
|
||||||
|
* ranksepFunc: undefined, // 每一层节点之间间距
|
||||||
|
* nodesep: 40, // 节点水平间距(px) 注意:如果有grid,需要保证nodesep为grid的偶数倍
|
||||||
|
* ranksep: 40, // 每一层节点之间间距 注意:如果有grid,需要保证ranksep为grid的偶数倍
|
||||||
|
* controlPoints: false, // 是否保留布局连线的控制点
|
||||||
|
* radial: false, // 是否基于 dagre 进行辐射布局
|
||||||
|
* focusNode: null, // radial 为 true 时生效,关注的节点
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
layout(option = {}) {
|
||||||
|
const { nodes, edges, gridSize } = this.lf.graphModel
|
||||||
|
// 为了保证生成的节点在girdSize上,需要处理一下。
|
||||||
|
let nodesep = 40
|
||||||
|
let ranksep = 40
|
||||||
|
if (gridSize > 20) {
|
||||||
|
nodesep = gridSize * 2
|
||||||
|
ranksep = gridSize * 2
|
||||||
|
}
|
||||||
|
this.option = {
|
||||||
|
type: 'dagre',
|
||||||
|
rankdir: 'LR',
|
||||||
|
// align: 'UL',
|
||||||
|
// align: 'UR',
|
||||||
|
align: 'DR',
|
||||||
|
nodesep,
|
||||||
|
ranksep,
|
||||||
|
begin: [120, 120],
|
||||||
|
...option
|
||||||
|
}
|
||||||
|
const layoutInstance = new DagreLayout(this.option)
|
||||||
|
const layoutData = layoutInstance.layout({
|
||||||
|
nodes: nodes.map((node: any) => ({
|
||||||
|
id: node.id,
|
||||||
|
size: {
|
||||||
|
width: node.width,
|
||||||
|
height: node.height
|
||||||
|
},
|
||||||
|
model: node
|
||||||
|
})),
|
||||||
|
edges: edges.map((edge: any) => ({
|
||||||
|
source: edge.sourceNodeId,
|
||||||
|
target: edge.targetNodeId,
|
||||||
|
model: edge
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
layoutData.nodes?.forEach((node: any) => {
|
||||||
|
// @ts-ignore: pass node data
|
||||||
|
const { model } = node
|
||||||
|
model.set_position({ x: node.x, y: node.y })
|
||||||
|
})
|
||||||
|
this.lf.fitView()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user