diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7eef6df..019c6c3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@ant-design/pro-components": "^2.8.10", + "@xyflow/react": "^12.8.4", "antd": "^5.27.1", "react": "^19.1.1", "react-dom": "^19.1.1", @@ -1909,6 +1910,49 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1925,7 +1969,7 @@ "version": "19.1.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.11.tgz", "integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==", - "dev": true, + "devOptional": true, "dependencies": { "csstype": "^3.0.2" } @@ -1974,6 +2018,36 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@xyflow/react": { + "version": "12.8.4", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.8.4.tgz", + "integrity": "sha512-bqUu4T5QSHiCFPkoH+b+LROKwQJdLvcjhGbNW9c1dLafCBRjmH1IYz0zPE+lRDXCtQ9kRyFxz3tG19+8VORJ1w==", + "dependencies": { + "@xyflow/system": "0.0.68", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.68", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.68.tgz", + "integrity": "sha512-QDG2wxIG4qX+uF8yzm1ULVZrcXX3MxPBoxv7O52FWsX87qIImOqifUhfa/TwsvLdzn7ic2DDBH1uI8TKbdNTYA==", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -2199,6 +2273,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -2277,6 +2356,102 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/dayjs": { "version": "1.11.14", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.14.tgz", @@ -4212,6 +4387,33 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/frontend/package.json b/frontend/package.json index 59d6fa0..bc39877 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@ant-design/pro-components": "^2.8.10", + "@xyflow/react": "^12.8.4", "antd": "^5.27.1", "react": "^19.1.1", "react-dom": "^19.1.1", diff --git a/frontend/src/pages/Flow/Detail.css b/frontend/src/pages/Flow/Detail.css new file mode 100644 index 0000000..ce80b59 --- /dev/null +++ b/frontend/src/pages/Flow/Detail.css @@ -0,0 +1,8 @@ +.flow-detail-right .node { + cursor: pointer; + border-radius: 8px; + margin-bottom: 8px; + padding: 16px 8px; + box-sizing: border-box; + text-align: center; +} \ No newline at end of file diff --git a/frontend/src/pages/Flow/Detail.jsx b/frontend/src/pages/Flow/Detail.jsx new file mode 100644 index 0000000..fdc007f --- /dev/null +++ b/frontend/src/pages/Flow/Detail.jsx @@ -0,0 +1,107 @@ +import { Button, Card, Col, Row, Typography } from "antd" +import { useNavigate } from "react-router-dom"; +import { useCallback } from 'react'; +import { + ReactFlow, + MiniMap, + Controls, + Background, + useNodesState, + useEdgesState, + addEdge, + Position, +} from '@xyflow/react'; +import '@xyflow/react/dist/style.css'; + +const COLORS = { + input: "#89A8B2", + git: "#B3C8CF", + cmd: "#E5E1DA", + status: "#F1F0E8" +} + +const initialNodes = [ + { + id: '1', + position: { x: 0, y: 0 }, + data: { label: '开始' }, + sourcePosition: 'right', + targetPosition: 'left', + type: 'input', + style: { + backgroundColor: COLORS["input"], + } + }, + { + id: '2', + position: { x: 200, y: 0 }, + data: { label: '2' }, + sourcePosition: 'right', + targetPosition: 'left', + }, +]; +const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }]; + + + +const FlowDetail = () => { + const navigate = useNavigate(); + const backButton = + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), [setEdges]); + const onBeforeDelete = async (params) => { + if (params?.nodes?.find(e => e.type === "input")) { + return false; + } + return true; + } + return + + +
+ + + + + +
+
+ + + + +
+} + +const RightCard = ({ onCreate}) => { + const nodes = [{ title: 'Git' }, {title: "Cmd"}, {title: "Status"}]; + return + {nodes?.map((e, i) => +
{ + onCreate && onCreate(e.title.toLowerCase()); + }} + > + + {e.title} + +
+ )} +
+} + +export default FlowDetail; \ No newline at end of file diff --git a/frontend/src/pages/Flow/components/StartNode.jsx b/frontend/src/pages/Flow/components/StartNode.jsx new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/pages/Flow/index.jsx b/frontend/src/pages/Flow/index.jsx index 5be9ce4..d9cad9f 100644 --- a/frontend/src/pages/Flow/index.jsx +++ b/frontend/src/pages/Flow/index.jsx @@ -4,11 +4,14 @@ import { useAuth } from "../../context/AuthContext"; import { LoadingOutlined } from "@ant-design/icons"; import { useEffect, useRef, useState } from "react"; import { tableList, flowRun, flowStop } from "../../api/flow"; +import { useNavigate } from "react-router-dom"; +import './Detail.css'; export default () => { const { user, logout } = useAuth(); const [status, setStatus] = useState({}); const eventSourceRef = useRef(null); + const navigate = useNavigate(); const needReload = useRef(false); const pageInit = useRef(false); const actionRef = useRef(null); @@ -80,7 +83,7 @@ export default () => { dataIndex: 'last_status', title: '最后执行结果', valueType: 'select', - fieldProps: { + fieldProps: { options: [ { value: 'RUNNING', "label": "执行中" }, { value: 'SUCCESS', "label": "成功" }, @@ -106,7 +109,9 @@ export default () => { ), - user?.level === 0 && , + user?.level === 0 && , user?.level === 0 && , ] } @@ -179,36 +184,4 @@ export default () => { ) -} - -const RunningStatus = ({record, status}) => { - const [open, setOpen] = useState(false); - - const current = record?.content?.findIndex(e => status?.[e.uuid]); - - console.log(status, current); - - return <> - setOpen(true)}> - - - 执行中 - - - setOpen(false)} - footer={[]} - > - - - } \ No newline at end of file diff --git a/frontend/src/routes.jsx b/frontend/src/routes.jsx index 89935c7..df79b59 100644 --- a/frontend/src/routes.jsx +++ b/frontend/src/routes.jsx @@ -6,6 +6,7 @@ import Login from "./pages/Login"; import User from "./pages/User"; import Profile from "./pages/User/Profile"; import Flow from "./pages/Flow"; +import FlowDetail from "./pages/Flow/Detail"; import LayoutPage from "./pages/Layout"; @@ -28,6 +29,7 @@ const routes = [ element: , children: [ { path: "/flow", element: }, + { path: "/flow/detail/:work_uuid", element: }, { path: "/user", element: }, { path: "/user/detail", element: } ]