diff --git a/src/api/is/project/index.js b/src/api/is/project/index.js index bfae294..046f703 100644 --- a/src/api/is/project/index.js +++ b/src/api/is/project/index.js @@ -6,4 +6,28 @@ export const getDictList = (params) => { method: 'get', params }) +} + +export const addProject = (data) => { + return request({ + url: '/ti/project', + method: 'post', + data + }) +} + +export const updateProject = (data) => { + return request({ + url: '/ti/project', + method: 'put', + data + }) +} + +export const getProjectList = (params) => { + return request({ + url: '/ti/project/list', + method: 'get', + params + }) } \ No newline at end of file diff --git a/src/views/flow/config.js b/src/views/flow/config.js index e741142..1eb684c 100644 --- a/src/views/flow/config.js +++ b/src/views/flow/config.js @@ -275,7 +275,7 @@ export const collapseList = [ { icon: expressionSvg, name: "HTTP请求", - type: "serviceNode", + type: "http", desc: "HTTP请求", action: 'HTTP', nodeParams: [ diff --git a/src/views/flow/nodes/function/httpNode.js b/src/views/flow/nodes/function/httpNode.js new file mode 100644 index 0000000..c642f73 --- /dev/null +++ b/src/views/flow/nodes/function/httpNode.js @@ -0,0 +1,219 @@ +// 导入 HtmlNode 及其模型,为后续继承做准备 +import { HtmlNode, HtmlNodeModel } from "@logicflow/core"; +// 导入 Vue 相关方法,用于渲染组件 +import { createApp, h, nextTick } from "vue"; +import ElementPlus from "element-plus"; +// 导入 Vue 组件 +// @ts-ignore +import HttpNode from "./httpNode.vue"; +import OuterNode from "../../components/OuterNode.vue"; +import JsonViewer from 'vue3-json-viewer' + +/** + * 定义一个 元素的 HTML 节点类,继承自 HtmlNode + * 该类负责在 HTML 中渲染 元素,并处理其交互逻辑 + */ +class HttpNodeHtmlNode extends HtmlNode { + resizeObserver = null; + isMounted; // 标记组件是否已挂载 + r; // 渲染函数 + app; // Vue 应用实例 + container = null; + + static reusePool = new Map(); // 节点复用池 + + /** + * 构造函数 + * @param props 传递给节点的属性,包括模型、图模型等 + */ + constructor(props) { + super(props); + this.initVueApp(props); + } + + initVueApp(props) { + this.isMounted = false; + + this.hideAnchor = false; + this.autoExpand = true; // 防止锚点被折叠 + this.anchorsPreset = "default"; // 重置锚点预设 + // 创建 元素的渲染函数 + this.r = h(OuterNode, { + model: props.model, + component: HttpNode, + properties: { + ...props.model.getProperties(), + }, + onContentChange: this.handleContentChange.bind(this), + onBindRef: this.handleComponentInstance.bind(this) + }); + + // 创建 Vue 应用实例,并指定渲染函数 + this.app = createApp({ + render: () => this.r, + }); + } + + /** + * 将 HTML 内容设置到指定的根元素上 + * @param rootEl 根元素 + */ + async setHtml(rootEl) { + const nodeId = this.props.model.id; + if (HttpNodeHtmlNode.reusePool.has(nodeId)) { + rootEl.appendChild(HttpNodeHtmlNode.reusePool.get(nodeId)); + return; + } + + if (!this.isMounted) { + this.isMounted = true; + this.container = document.createElement("div"); + this.container.style.display = "inline-block"; // 关键:确保容器自适应内容 + + rootEl.appendChild(this.container); + this.app.use(ElementPlus); + this.app.use(JsonViewer); + + this.app.mount(this.container); + await nextTick(); + this.setupSizeObserver(); + HttpNodeHtmlNode.reusePool.set(nodeId, this.container); + } else { + this.r.component.props.properties = this.props.model.getProperties(); + } + } + + /** + * 获取节点文本内容 + * 对于元素,返回 null,因为其内容由特定组件渲染 + * @returns {null} + */ + getText() { + return null; + } + + handleComponentInstance(data) { + // 将组件实例保存到节点模型 + this.props.model.setComponentInstance(data) + } + + handleContentChange() { + // 内容变化时强制更新尺寸 + this.updateNodeSize(); + } + + // 渲染完成后获取实际尺寸 + updateNodeSize() { + if (this.container) { + const { SCALE_X, SCALE_Y } = this.props.graphModel.transformModel; + const node = this.container.querySelector(".node__container"); + const rect = node.getBoundingClientRect(); + this.props.model.updateSize(rect.width / SCALE_X, rect.height / SCALE_Y); + } + } + + setupSizeObserver() { + // 首次渲染立即检测 + requestAnimationFrame(() => { + this.updateNodeSize(); + // 持续监听变化 + this.resizeObserver = new ResizeObserver(() => { + this.updateNodeSize(); + }); + this.resizeObserver.observe(this.container); + }); + } + + // 组件卸载时移除监听 + onDestroy() { + this.resizeObserver?.disconnect(); + } +} + +/** + * 定义一个元素的 HTML 模型类,继承自 HtmlNodeModel + * 该类主要设置节点的属性和样式 + */ +class HttpNodeHtmlModel extends HtmlNodeModel { + initNodeData(data) { + super.initNodeData(data); + } + + // 保存组件实例引用 + setComponentInstance(instance) { + this.componentInstance = instance; + } + + // 验证表单 + async validateForm() { + const result = this.componentInstance.validateForm() + return result + } + + setCustomProperties() { + this.componentInstance.setNodeProperties() + } + + /** + * 设置节点属性 + * 包括宽度、高度、文本编辑属性等 + */ + setAttributes() { + // 初始设置为0,后续动态更新 + this.width = 0; + this.height = 0; + this.text.editable = false; + } + + updateSize(width, height) { + this.width = width + 24; + this.height = height + 50; + this.initNodeData(this); // 触发节点重绘 + } + + // 定义节点只有左右两个锚点. 锚点位置通过中心点和宽度算出来。 + getDefaultAnchor() { + let _a = this, + x = _a.x, + y = _a.y, + width = _a.width, + height = _a.height; + return [ + { + x: x + width / 2 + 4, + y: y, + name: "right", + id: "".concat(this.id, "_1"), + properties: { connectionType: "source" }, + }, + { + x: x - width / 2, + y: y, + name: "left", + id: "".concat(this.id, "_3"), + properties: { connectionType: "target" }, + }, + ]; + } + + /** + * 获取节点轮廓样式 + * 覆盖父类方法,设置 stroke 属性为 none,以适应特定的视觉效果 + * @returns {object} 节点轮廓样式 + */ + getOutlineStyle() { + const style = super.getOutlineStyle(); + style.stroke = "none"; + style.hover.stroke = "none"; + return style; + } +} + +// 导出方法注册 +export function registerHttpNode(lf) { + lf.register({ + type: "http", + view: HttpNodeHtmlNode, + model: HttpNodeHtmlModel, + }); +} diff --git a/src/views/flow/nodes/function/httpNode.vue b/src/views/flow/nodes/function/httpNode.vue new file mode 100644 index 0000000..1135f92 --- /dev/null +++ b/src/views/flow/nodes/function/httpNode.vue @@ -0,0 +1,612 @@ + + + + + diff --git a/src/views/flow/nodes/function/index.js b/src/views/flow/nodes/function/index.js index 61f2fb3..9f26f42 100644 --- a/src/views/flow/nodes/function/index.js +++ b/src/views/flow/nodes/function/index.js @@ -4,6 +4,7 @@ import { registerSwitch } from './switch' import { registerSubEndNode } from './subEnd' import { registerSubStartNode } from './subStart' import { registerSleepNode } from './sleep' +import { registerHttpNode } from './httpNode' export const registerFunction = (lf) => { registerLoopBodyNode(lf) @@ -12,4 +13,5 @@ export const registerFunction = (lf) => { registerSubEndNode(lf) registerSubStartNode(lf) registerSleepNode(lf) + registerHttpNode(lf) } \ No newline at end of file diff --git a/src/views/is/project/index.vue b/src/views/is/project/index.vue index 7d84774..3b862d1 100644 --- a/src/views/is/project/index.vue +++ b/src/views/is/project/index.vue @@ -118,13 +118,6 @@ fixed="right" >