hookehuyr

🎉 init: 项目初始化

{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"DirectiveBinding": true,
"EffectScope": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"PropType": true,
"Ref": true,
"VNode": true,
"WritableComputedRef": true,
"computed": true,
"createApp": true,
"customRef": true,
"defineAsyncComponent": true,
"defineComponent": true,
"effectScope": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeRouteLeave": true,
"onBeforeRouteUpdate": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onUnmounted": true,
"onUpdated": true,
"onWatcherCleanup": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useId": true,
"useLink": true,
"useModel": true,
"useRoute": true,
"useRouter": true,
"useSlots": true,
"useTemplateRef": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
"watchSyncEffect": true
}
}
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.history
{
"recommendations": ["Vue.volar"]
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
This diff is collapsed. Click to expand it.
{
"name": "logic-flow2",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@logicflow/core": "^2.0.11",
"@logicflow/engine": "^0.1.1",
"@logicflow/extension": "^2.0.15",
"@logicflow/vue-node-registry": "^1.0.13",
"autoprefixer": "^10.4.21",
"echarts": "^5.6.0",
"element-plus": "^2.9.6",
"postcss": "^8.5.3",
"tailwindcss": "^4.0.12",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",
"sass-embedded": "^1.85.1",
"unplugin-auto-import": "^19.1.1",
"vite": "^6.2.0",
"vite-plugin-dynamic-import": "^1.6.0"
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
<!--
* @Date: 2025-03-10 13:07:05
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-10 14:40:31
* @FilePath: /logic-flow2/src/App.vue
* @Description: 文件描述
-->
<script setup>
</script>
<template>
<router-view></router-view>
</template>
<style scoped>
</style>
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useId: typeof import('vue')['useId']
const useLink: typeof import('vue-router')['useLink']
const useModel: typeof import('vue')['useModel']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
// for vue template auto import
import { UnwrapRef } from 'vue'
declare module 'vue' {
interface GlobalComponents {}
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router')['onBeforeRouteUpdate']>
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
readonly ref: UnwrapRef<typeof import('vue')['ref']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
readonly unref: UnwrapRef<typeof import('vue')['unref']>
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
readonly useId: UnwrapRef<typeof import('vue')['useId']>
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
readonly watch: UnwrapRef<typeof import('vue')['watch']>
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
}
}
\ No newline at end of file
/*
* @Date: 2025-03-10 16:20:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-10 16:33:34
* @FilePath: /logic-flow2/src/components/logicflow/custom-node.ts
* @Description: 文件描述
*/
// src/components/logicflow/CustomNode.ts
import { RectNode, RectNodeModel } from "@logicflow/core";
// 自定义模型
export class CustomModel extends RectNodeModel {
setAttributes() {
this.stroke = "#1E90FF";
this.fill = "#F0F8FF";
this.radius = 10;
const { isDisabledNode } = this.properties;
// 动态菜单配置
if (!isDisabledNode) {
this.menu = [
{
className: "lf-menu-delete",
icon: true,
callback: (node) => {
this.graphModel.deleteNode(node.id);
this.graphModel.eventCenter.emit("custom:event", node);
},
},
{
text: "Edit",
className: "lf-menu-item",
callback: (node) => {
this.graphModel.setElementStateById(node.id, 2);
},
},
{
text: "Copy",
className: "lf-menu-item",
callback: (node) => {
this.graphModel.cloneNode(node.id);
},
},
];
}
}
}
// 使用默认矩形视图
export const CustomNode = RectNode;
/*
* @Date: 2025-03-10 13:07:05
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-10 15:50:42
* @FilePath: /logic-flow2/src/main.js
* @Description: 文件描述
*/
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import "@logicflow/core/lib/style/index.css";
import '@logicflow/extension/lib/style/index.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import LogicFlow from '@logicflow/core';
import { Menu } from "@logicflow/extension";
LogicFlow.use(Menu) // 右键菜单
const app = createApp(App)
app.use(ElementPlus)
app.use(router)
app.mount('#app')
/*
* @Date: 2025-03-10 13:15:30
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-10 15:49:17
* @FilePath: /logic-flow2/src/router/index.js
* @Description: 文件描述
*/
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: () => import('../views/Home.vue')
},
{
path: '/menu',
name: 'menu',
component: () => import('../views/menu.vue')
},
]
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 这里可以添加路由导航守卫的逻辑
next()
})
export default router
File mode changed
/**
* 获取随机整数 [min, max],不包含 [excludemMin, excludeMax]
* @param min
* @param max
* @returns
*/
export const getRandom = (min: number, max: number, excludemMin?: number, excludeMax?: number) => {
let res = Math.floor(Math.random() * (max - min + 1) + min)
if (
excludemMin !== undefined &&
excludeMax !== undefined &&
res >= excludemMin &&
res <= excludeMax
) {
res = getRandom(min, max, excludemMin, excludeMax)
}
return res
}
/**
* 获取页面 dom 数量
* @returns
*/
export const getTotalDOMNumber = () => document.querySelectorAll('*').length
export type PerformanceLongTaskEntry = {
type: 'longTask'
eventType: string
startTime: number
duration: number
}
/**
* 监控长任务事件响应时间:耗费了 50 毫秒或更多时间
*/
export const startObservingLongTasks = (callback: any) => {
const observer = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries()
entries.forEach((entry) => {
requestIdleCallback(() => {
callback(entry)
})
})
})
observer.observe({ entryTypes: ['longtask'] })
}
<!--
* @Date: 2025-03-10 14:37:31
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-10 15:47:42
* @FilePath: /logic-flow2/src/views/Home.vue
* @Description: 文件描述
-->
<template>
<div>插件</div>
<el-button type="primary" @click="goTo('menu')">Menu</el-button>
</template>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const goTo = (name) => {
router.push({ name })
}
</script>
<style scoped>
</style>
<!--
* @Date: 2025-03-10 14:37:31
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-10 16:38:24
* @FilePath: /logic-flow2/src/views/menu.vue
* @Description: 文件描述
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from '@logicflow/core';
import { CustomNode, CustomModel } from '@components/logicflow/custom-node.js'; // 指定业务状态设置菜单
const container = ref(null);
let lf = null;
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
});
// 为菜单追加选项(必须在 lf.render() 之前设置)
// 或者直接通过 lf.addMenuConfig 也可以调用
lf.extension.menu.addMenuConfig({
nodeMenu: [
{
text: '分享',
callback() {
alert('分享成功!')
},
},
{
text: '属性',
callback(node) {
alert(`
节点id:${node.id}
节点类型:${node.type}
节点坐标:(x: ${node.x}, y: ${node.y})
`)
},
},
],
edgeMenu: [
{
text: '属性',
callback(edge) {
const {
id,
type,
startPoint,
endPoint,
sourceNodeId,
targetNodeId,
} = edge
alert(`
边id:${id}
边类型:${type}
边起点坐标:(startPoint: [${startPoint.x}, ${startPoint.y}])
边终点坐标:(endPoint: [${endPoint.x}, ${endPoint.y}])
源节点id:${sourceNodeId}
目标节点id:${targetNodeId}
`)
},
},
],
graphMenu: [
{
text: '分享',
callback() {
alert('分享成功!')
},
},
],
})
// 如果默认菜单中存在不需要的选项,或者无法满足需求,可以通过lf.setMenuConfig重置菜单,更换为自定义菜单。
lf.extension.menu.setMenuConfig({
nodeMenu: [
{
text: "删除",
callback(node) {
lf.deleteNode(node.id);
},
},
], // 覆盖默认的节点右键菜单
edgeMenu: false, // 删除默认的边右键菜单
graphMenu: [], // 覆盖默认的边右键菜单,与false表现一样
});
// 注册自定义节点
lf.register({
type: "custom_node",
view: CustomNode,
model: CustomModel,
});
// 监听自定义事件
lf.on('custom:event', (nodeData) => {
console.log('Node deleted:', nodeData);
// 这里可以添加业务逻辑
});
lf.render({
nodes: [
{ id: 'node1', type: 'rect', x: 100, y: 100 },
{ id: 'node2', type: 'circle', x: 300, y: 100 },
{
id: 'node3',
type: 'custom_node',
x: 500,
y: 100,
properties: {
isDisabledNode: false
}
}
],
edges: [{ id: 'edge1', sourceNodeId: 'node1', targetNodeId: 'node2' }],
});
});
</script>
<style scoped>
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.flow-container {
flex: 1;
width: 100%;
height: 100%;
}
/* 自定义菜单样式 */
.lf-menu-item {
padding: 8px;
cursor: pointer;
&:hover {
background: #f0f8ff;
}
}
</style>
/*
* @Date: 2025-03-10 13:07:05
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-10 16:49:02
* @FilePath: /logic-flow2/vite.config.js
* @Description: 文件描述
*/
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import dynamicImport from 'vite-plugin-dynamic-import';
import path from 'path';
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
dynamicImport(), // 增强 Vite 内置的 dynamic import, 支持在 import() 中使用别名
AutoImport({
imports: ['vue', 'vue-router'],
dts: 'src/auto-imports.d.ts',
dirs: ['src/composables', 'src/stores'],
vueTemplate: true,
eslintrc: {
enabled: true,
},
}),
],
resolve: {
alias: {
'@': '/src',
'@components': path.resolve(__dirname, 'src/components'),
},
},
})
This diff is collapsed. Click to expand it.