mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2026-03-05 15:05:27 +00:00
固定useStore核心功能,调用解耦,优化代码
This commit is contained in:
@@ -82,6 +82,7 @@ import updateLogs from "../data/updateLog.json"
|
||||
import {useFilesStore} from "@/ts/useStore";
|
||||
import {ElMessageBox} from "element-plus";
|
||||
import {useGlobalMessage} from "@/ts/useGlobalMessage";
|
||||
import { getLogicFlowInstance } from "@/ts/useLogicFlow";
|
||||
// import { useScreenshot } from '@/ts/useScreenshot';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
|
||||
@@ -100,6 +101,23 @@ const state = reactive({
|
||||
showFeedbackFormDialog: false, // 控制反馈表单对话框的显示状态
|
||||
});
|
||||
|
||||
// 重新渲染 LogicFlow 画布的通用方法
|
||||
const refreshLogicFlowCanvas = (message?: string) => {
|
||||
setTimeout(() => {
|
||||
const logicFlowInstance = getLogicFlowInstance();
|
||||
if (logicFlowInstance) {
|
||||
// 获取当前活动文件的数据
|
||||
const currentFileData = filesStore.getTab(filesStore.activeFile);
|
||||
if (currentFileData) {
|
||||
// 清空画布并重新渲染
|
||||
logicFlowInstance.clearData();
|
||||
logicFlowInstance.render(currentFileData);
|
||||
console.log(message || 'LogicFlow 画布已重新渲染');
|
||||
}
|
||||
}
|
||||
}, 100); // 延迟一点确保数据更新完成
|
||||
};
|
||||
|
||||
const loadExample = () => {
|
||||
ElMessageBox.confirm(
|
||||
'加载样例会覆盖当前数据,是否覆盖?',
|
||||
@@ -133,6 +151,7 @@ const loadExample = () => {
|
||||
activeFile: "example"
|
||||
};
|
||||
filesStore.importData(defaultState);
|
||||
refreshLogicFlowCanvas('LogicFlow 画布已重新渲染(示例数据)');
|
||||
showMessage('success', '数据已恢复');
|
||||
}).catch(() => {
|
||||
showMessage('info', '选择了不恢复旧数据');
|
||||
@@ -161,7 +180,13 @@ const showFeedbackForm = () => {
|
||||
};
|
||||
|
||||
const handleExport = () => {
|
||||
filesStore.exportData();
|
||||
// 导出前先更新当前数据,确保不丢失最新修改
|
||||
filesStore.updateTab();
|
||||
|
||||
// 延迟一点确保更新完成后再导出
|
||||
setTimeout(() => {
|
||||
filesStore.exportData();
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
const handleImport = () => {
|
||||
@@ -178,6 +203,7 @@ const handleImport = () => {
|
||||
const target = e.target as FileReader;
|
||||
const data = JSON.parse(target.result as string);
|
||||
filesStore.importData(data);
|
||||
// refreshLogicFlowCanvas('LogicFlow 画布已重新渲染(导入数据)');
|
||||
} catch (error) {
|
||||
console.error('Failed to import file', error);
|
||||
showMessage('error', '文件格式错误');
|
||||
|
||||
@@ -36,13 +36,9 @@ import { useFilesStore } from "@/ts/useStore";
|
||||
import { setLogicFlowInstance, destroyLogicFlowInstance } from '@/ts/useLogicFlow';
|
||||
|
||||
const props = defineProps<{
|
||||
nodes: any[];
|
||||
edges: any[];
|
||||
viewport?: { x: number; y: number; zoom: number };
|
||||
height?: string;
|
||||
}>();
|
||||
|
||||
const filesStore = useFilesStore();
|
||||
const containerRef = ref<HTMLElement | null>(null);
|
||||
const lf = ref<LogicFlow | null>(null);
|
||||
|
||||
@@ -70,13 +66,17 @@ function registerNodes(lfInstance: LogicFlow) {
|
||||
// 初始化 LogicFlow
|
||||
onMounted(() => {
|
||||
lf.value = new LogicFlow({
|
||||
container: containerRef.value as HTMLElement,
|
||||
container: containerRef.value,
|
||||
// container: document.querySelector('#container'),
|
||||
grid: true,
|
||||
allowResize: true,
|
||||
allowRotate : true
|
||||
});
|
||||
registerNodes(lf.value);
|
||||
renderFlow();
|
||||
setLogicFlowInstance(lf.value);
|
||||
|
||||
lf.value.render({
|
||||
// 渲染的数据
|
||||
})
|
||||
// 监听节点点击事件,更新 selectedNode
|
||||
lf.value.on(EventType.NODE_CLICK, ({ data }) => {
|
||||
selectedNode.value = data;
|
||||
@@ -112,46 +112,8 @@ onBeforeUnmount(() => {
|
||||
destroyLogicFlowInstance();
|
||||
});
|
||||
|
||||
// 响应式更新 nodes/edges
|
||||
// watch(
|
||||
// () => [props.nodes, props.edges],
|
||||
// () => {
|
||||
// renderFlow();
|
||||
// },
|
||||
// { deep: true }
|
||||
// );
|
||||
|
||||
// 响应式更新 viewport
|
||||
watch(
|
||||
() => props.viewport,
|
||||
(val) => {
|
||||
if (val) setViewport(val);
|
||||
}
|
||||
);
|
||||
|
||||
function renderFlow() {
|
||||
if (!lf.value) return;
|
||||
lf.value.render({
|
||||
nodes: props.nodes,
|
||||
edges: props.edges,
|
||||
});
|
||||
}
|
||||
|
||||
function setViewport(viewport?: { x: number; y: number; zoom: number }) {
|
||||
if (!lf.value || !viewport) return;
|
||||
lf.value.zoom(viewport.zoom);
|
||||
// lf.value.focusOn({ x: viewport.x, y: viewport.y });
|
||||
}
|
||||
|
||||
function getViewport() {
|
||||
if (!lf.value) return { x: 0, y: 0, zoom: 1 };
|
||||
const t = lf.value.getTransform();
|
||||
return {
|
||||
x: t.TRANSLATE_X,
|
||||
y: t.TRANSLATE_Y,
|
||||
zoom: t.SCALE_X
|
||||
};
|
||||
}
|
||||
|
||||
// 右键菜单相关
|
||||
function handleNodeContextMenu({ data, e }: { data: any; e: MouseEvent }) {
|
||||
@@ -174,23 +136,6 @@ function handleLayerOrder(action: string) {
|
||||
contextMenu.value.show = false;
|
||||
}
|
||||
|
||||
function getGraphRawData() {
|
||||
if (!lf) return null;
|
||||
return lf.value.getGraphRawData();
|
||||
}
|
||||
|
||||
function renderRawData(data: any) {
|
||||
if (!lf) return;
|
||||
lf.value.renderRawData(data);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getViewport,
|
||||
setViewport,
|
||||
renderFlow,
|
||||
getGraphRawData,
|
||||
renderRawData,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, watch} from 'vue';
|
||||
import {Handle, useVueFlow} from '@vue-flow/core';
|
||||
import {NodeResizer} from '@vue-flow/node-resizer';
|
||||
import '@vue-flow/node-resizer/dist/style.css';
|
||||
<!--<script setup lang="ts">-->
|
||||
<!--import {ref, watch} from 'vue';-->
|
||||
<!--import {Handle, useVueFlow} from '@vue-flow/core';-->
|
||||
<!--import {NodeResizer} from '@vue-flow/node-resizer';-->
|
||||
<!--import '@vue-flow/node-resizer/dist/style.css';-->
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
id: String,
|
||||
selected: Boolean
|
||||
});
|
||||
<!--const props = defineProps({-->
|
||||
<!-- data: Object,-->
|
||||
<!-- id: String,-->
|
||||
<!-- selected: Boolean-->
|
||||
<!--});-->
|
||||
|
||||
const nodeWidth = ref(180);
|
||||
const nodeHeight = ref(120);
|
||||
<!--const nodeWidth = ref(180);-->
|
||||
<!--const nodeHeight = ref(120);-->
|
||||
|
||||
// 监听props.data变化,支持外部更新图片
|
||||
watch(() => props.data, (newData) => {
|
||||
if (newData && newData.width) nodeWidth.value = newData.width;
|
||||
if (newData && newData.height) nodeHeight.value = newData.height;
|
||||
}, {immediate: true});
|
||||
<!--// 监听props.data变化,支持外部更新图片-->
|
||||
<!--watch(() => props.data, (newData) => {-->
|
||||
<!-- if (newData && newData.width) nodeWidth.value = newData.width;-->
|
||||
<!-- if (newData && newData.height) nodeHeight.value = newData.height;-->
|
||||
<!--}, {immediate: true});-->
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<NodeResizer v-if="selected" :min-width="60" :min-height="60" :max-width="400" :max-height="400"/>
|
||||
<div class="image-node">
|
||||
<Handle type="target" position="left" :id="`${id}-target`"/>
|
||||
<div class="image-content">
|
||||
<img v-if="props.data && props.data.url" :src="props.data.url" alt="图片节点"
|
||||
style="width:100%;height:100%;object-fit:contain;"/>
|
||||
<div v-else class="image-placeholder">未上传图片</div>
|
||||
</div>
|
||||
<Handle type="source" position="right" :id="`${id}-source`"/>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.image-node {
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 180px;
|
||||
min-height: 180px;
|
||||
}
|
||||
<!--</script>-->
|
||||
<!--<template>-->
|
||||
<!-- <NodeResizer v-if="selected" :min-width="60" :min-height="60" :max-width="400" :max-height="400"/>-->
|
||||
<!-- <div class="image-node">-->
|
||||
<!-- <Handle type="target" position="left" :id="`${id}-target`"/>-->
|
||||
<!-- <div class="image-content">-->
|
||||
<!-- <img v-if="props.data && props.data.url" :src="props.data.url" alt="图片节点"-->
|
||||
<!-- style="width:100%;height:100%;object-fit:contain;"/>-->
|
||||
<!-- <div v-else class="image-placeholder">未上传图片</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <Handle type="source" position="right" :id="`${id}-source`"/>-->
|
||||
<!-- </div>-->
|
||||
<!--</template>-->
|
||||
<!--<style scoped>-->
|
||||
<!--.image-node {-->
|
||||
<!-- background: #fff;-->
|
||||
<!-- border: 1px solid #dcdfe6;-->
|
||||
<!-- border-radius: 4px;-->
|
||||
<!-- display: flex;-->
|
||||
<!-- flex-direction: column;-->
|
||||
<!-- align-items: center;-->
|
||||
<!-- justify-content: center;-->
|
||||
<!-- overflow: hidden;-->
|
||||
<!-- position: relative;-->
|
||||
<!-- width: 100%;-->
|
||||
<!-- height: 100%;-->
|
||||
<!-- min-width: 180px;-->
|
||||
<!-- min-height: 180px;-->
|
||||
<!--}-->
|
||||
|
||||
.image-content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
<!--.image-content {-->
|
||||
<!-- position: relative;-->
|
||||
<!-- width: 100%;-->
|
||||
<!-- height: 100%;-->
|
||||
<!-- display: flex;-->
|
||||
<!-- align-items: center;-->
|
||||
<!-- justify-content: center;-->
|
||||
<!--}-->
|
||||
|
||||
.image-placeholder {
|
||||
color: #bbb;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
<!--.image-placeholder {-->
|
||||
<!-- color: #bbb;-->
|
||||
<!-- font-size: 14px;-->
|
||||
<!--}-->
|
||||
<!--</style>-->
|
||||
@@ -1,51 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { Handle, useVueFlow } from '@vue-flow/core';
|
||||
import { NodeResizer } from '@vue-flow/node-resizer';
|
||||
import '@vue-flow/node-resizer/dist/style.css';
|
||||
<!--<script setup lang="ts">-->
|
||||
<!--import { ref, watch } from 'vue';-->
|
||||
<!--import { Handle, useVueFlow } from '@vue-flow/core';-->
|
||||
<!--import { NodeResizer } from '@vue-flow/node-resizer';-->
|
||||
<!--import '@vue-flow/node-resizer/dist/style.css';-->
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
id: String,
|
||||
selected: Boolean
|
||||
});
|
||||
<!--const props = defineProps({-->
|
||||
<!-- data: Object,-->
|
||||
<!-- id: String,-->
|
||||
<!-- selected: Boolean-->
|
||||
<!--});-->
|
||||
|
||||
const nodeWidth = ref(200);
|
||||
const nodeHeight = ref(120);
|
||||
const html = ref('');
|
||||
<!--const nodeWidth = ref(200);-->
|
||||
<!--const nodeHeight = ref(120);-->
|
||||
<!--const html = ref('');-->
|
||||
|
||||
watch(() => props.data, (newData) => {
|
||||
if (newData && newData.html !== undefined) html.value = newData.html;
|
||||
if (newData && newData.width) nodeWidth.value = newData.width;
|
||||
if (newData && newData.height) nodeHeight.value = newData.height;
|
||||
}, { immediate: true });
|
||||
</script>
|
||||
<template>
|
||||
<div class="text-node" :style="{ width: `${nodeWidth}px`, height: `${nodeHeight}px` }">
|
||||
<NodeResizer v-if="selected" :min-width="80" :min-height="40" :max-width="400" :max-height="400" />
|
||||
<Handle type="target" position="left" :id="`${id}-target`" />
|
||||
<div class="text-content" v-html="html"></div>
|
||||
<Handle type="source" position="right" :id="`${id}-source`" />
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.text-node {
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.text-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
word-break: break-all;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
<!--watch(() => props.data, (newData) => {-->
|
||||
<!-- if (newData && newData.html !== undefined) html.value = newData.html;-->
|
||||
<!-- if (newData && newData.width) nodeWidth.value = newData.width;-->
|
||||
<!-- if (newData && newData.height) nodeHeight.value = newData.height;-->
|
||||
<!--}, { immediate: true });-->
|
||||
<!--</script>-->
|
||||
<!--<template>-->
|
||||
<!-- <div class="text-node" :style="{ width: `${nodeWidth}px`, height: `${nodeHeight}px` }">-->
|
||||
<!-- <NodeResizer v-if="selected" :min-width="80" :min-height="40" :max-width="400" :max-height="400" />-->
|
||||
<!-- <Handle type="target" position="left" :id="`${id}-target`" />-->
|
||||
<!-- <div class="text-content" v-html="html"></div>-->
|
||||
<!-- <Handle type="source" position="right" :id="`${id}-source`" />-->
|
||||
<!-- </div>-->
|
||||
<!--</template>-->
|
||||
<!--<style scoped>-->
|
||||
<!--.text-node {-->
|
||||
<!-- background: #fff;-->
|
||||
<!-- border: 1px solid #dcdfe6;-->
|
||||
<!-- border-radius: 4px;-->
|
||||
<!-- display: flex;-->
|
||||
<!-- flex-direction: column;-->
|
||||
<!-- align-items: center;-->
|
||||
<!-- justify-content: center;-->
|
||||
<!-- overflow: hidden;-->
|
||||
<!--}-->
|
||||
<!--.text-content {-->
|
||||
<!-- width: 100%;-->
|
||||
<!-- height: 100%;-->
|
||||
<!-- padding: 8px;-->
|
||||
<!-- font-size: 15px;-->
|
||||
<!-- color: #333;-->
|
||||
<!-- word-break: break-all;-->
|
||||
<!-- overflow: auto;-->
|
||||
<!--}-->
|
||||
<!--</style>-->
|
||||
Reference in New Issue
Block a user