diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ef4a4968..7828b7f2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -23,6 +23,7 @@ "@logicflow/core": "^2.0.9", "@logicflow/extension": "^2.0.13", "@reduxjs/toolkit": "^2.0.1", + "@types/recharts": "^1.8.29", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "antd": "^5.22.2", @@ -33,7 +34,8 @@ "react-diff-viewer-continued": "^3.4.0", "react-dom": "^18.2.0", "react-redux": "^9.0.4", - "react-router-dom": "^6.21.0" + "react-router-dom": "^6.21.0", + "recharts": "^2.15.0" }, "devDependencies": { "@types/dagre": "^0.7.52", @@ -1851,6 +1853,60 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "1.0.11", + "resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-1.0.11.tgz", + "integrity": "sha512-4pQMp8ldf7UaB/gR8Fvvy69psNHkTpD/pVw3vmEi8iZAB9EPMBruB1JvHO4BIq9QkUUd2lV1F5YXpMNj7JPBpw==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "1.3.12", + "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-1.3.12.tgz", + "integrity": "sha512-8oMzcd4+poSLGgV0R1Q1rOlx/xdmozS4Xab7np0eamFFUYq71AU9pOCJEFnkXW2aI/oXdVYJzw6pssbSut7Z9Q==", + "dependencies": { + "@types/d3-path": "^1" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "node_modules/@types/dagre": { "version": "0.7.52", "resolved": "https://registry.npmmirror.com/@types/dagre/-/dagre-0.7.52.tgz", @@ -1905,14 +1961,12 @@ "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1927,6 +1981,15 @@ "@types/react": "*" } }, + "node_modules/@types/recharts": { + "version": "1.8.29", + "resolved": "https://registry.npmmirror.com/@types/recharts/-/recharts-1.8.29.tgz", + "integrity": "sha512-ulKklaVsnFIIhTQsQw226TnOibrddW1qUQNFVhoQEyY1Z7FRQrNecFCGt7msRuJseudzE9czVawZb17dK/aPXw==", + "dependencies": { + "@types/d3-shape": "^1", + "@types/react": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.5.8.tgz", @@ -2674,6 +2737,14 @@ "resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz", "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", @@ -2855,11 +2926,30 @@ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-binarytree": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/d3-binarytree/-/d3-binarytree-1.0.2.tgz", "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==" }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz", @@ -2868,6 +2958,14 @@ "node": ">=12" } }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-force": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz", @@ -2896,11 +2994,38 @@ "node": ">=12" } }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/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-octree": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/d3-octree/-/d3-octree-1.0.2.tgz", "integrity": "sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA==" }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-quadtree": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz", @@ -2909,6 +3034,54 @@ "node": ">=12" } }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-timer": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", @@ -2948,6 +3121,11 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", @@ -3000,6 +3178,15 @@ "resolved": "https://registry.npmmirror.com/dom-align/-/dom-align-1.12.4.tgz", "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==" }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.64", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.64.tgz", @@ -3340,6 +3527,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", @@ -3354,6 +3546,14 @@ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz", @@ -3851,6 +4051,14 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/intersection-observer": { "version": "0.12.2", "resolved": "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz", @@ -5488,6 +5696,65 @@ "react-dom": ">=16.8" } }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmmirror.com/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/recharts": { + "version": "2.15.0", + "resolved": "https://registry.npmmirror.com/recharts/-/recharts-2.15.0.tgz", + "integrity": "sha512-cIvMxDfpAmqAmVgc4yb7pgm/O1tmmkl/CjrvXuW+62/+7jj/iF9Ykm+hb/UJt42TREHMyd3gb+pkgoa2MxgDIw==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmmirror.com/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz", @@ -5971,6 +6238,11 @@ "node": ">=12.22" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, "node_modules/tinycolor2": { "version": "1.6.0", "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz", @@ -6126,6 +6398,35 @@ "@sphinxxxx/color-conversion": "^2.2.2" } }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/victory-vendor/node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, "node_modules/virtualizedtableforantd4": { "version": "1.3.1", "resolved": "https://registry.npmmirror.com/virtualizedtableforantd4/-/virtualizedtableforantd4-1.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 63dcaee2..061ac502 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,7 @@ "@logicflow/core": "^2.0.9", "@logicflow/extension": "^2.0.13", "@reduxjs/toolkit": "^2.0.1", + "@types/recharts": "^1.8.29", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "antd": "^5.22.2", @@ -35,7 +36,8 @@ "react-diff-viewer-continued": "^3.4.0", "react-dom": "^18.2.0", "react-redux": "^9.0.4", - "react-router-dom": "^6.21.0" + "react-router-dom": "^6.21.0", + "recharts": "^2.15.0" }, "devDependencies": { "@types/dagre": "^0.7.52", diff --git a/frontend/src/pages/Dashboard/index.tsx b/frontend/src/pages/Dashboard/index.tsx index 79abf74a..e2dbfb4d 100644 --- a/frontend/src/pages/Dashboard/index.tsx +++ b/frontend/src/pages/Dashboard/index.tsx @@ -1,47 +1,234 @@ -import React, { useEffect } from 'react'; -import { Card, Row, Col, Statistic } from 'antd'; -import { UserOutlined, ShoppingCartOutlined, FileOutlined } from '@ant-design/icons'; +import React from 'react'; +import { Card, Row, Col, Statistic, Table, Tag, Space, Progress, List, Avatar } from 'antd'; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; +import { RocketOutlined, CloudServerOutlined, CheckCircleOutlined, ClockCircleOutlined } from '@ant-design/icons'; + +// Mock 数据 +const mockDeploymentStats = { + totalDeployments: 1892, + successRate: 98.5, + activeEnvironments: 12, + averageDeployTime: '3.5分钟' +}; + +const mockDeploymentTrend = [ + { date: '12-11', deployments: 35, success: 33, failed: 2 }, + { date: '12-12', deployments: 42, success: 40, failed: 2 }, + { date: '12-13', deployments: 28, success: 28, failed: 0 }, + { date: '12-14', deployments: 45, success: 43, failed: 2 }, + { date: '12-15', deployments: 50, success: 48, failed: 2 }, + { date: '12-16', deployments: 38, success: 37, failed: 1 }, + { date: '12-17', deployments: 40, success: 39, failed: 1 } +]; + +const mockEnvironments = [ + { + name: '生产环境', + status: 'HEALTHY', + lastDeployment: '2024-12-17 20:30:00', + resourceUsage: 78, + services: 15 + }, + { + name: 'UAT环境', + status: 'HEALTHY', + lastDeployment: '2024-12-17 19:45:00', + resourceUsage: 65, + services: 15 + }, + { + name: '测试环境', + status: 'WARNING', + lastDeployment: '2024-12-17 18:20:00', + resourceUsage: 85, + services: 12 + }, + { + name: '开发环境', + status: 'HEALTHY', + lastDeployment: '2024-12-17 17:15:00', + resourceUsage: 45, + services: 15 + } +]; + +const mockRecentActivities = [ + { + id: 1, + type: 'deployment', + project: '用户中心服务', + environment: '生产环境', + status: 'SUCCESS', + operator: '张三', + time: '10分钟前', + version: 'v2.3.0' + }, + { + id: 2, + type: 'deployment', + project: '订单服务', + environment: 'UAT环境', + status: 'IN_PROGRESS', + operator: '李四', + time: '25分钟前', + version: 'v1.8.5' + }, + { + id: 3, + type: 'deployment', + project: '支付服务', + environment: '测试环境', + status: 'FAILED', + operator: '王五', + time: '1小时前', + version: 'v2.1.0' + } +]; const Dashboard: React.FC = () => { - useEffect(() => { - // 检查这里的数据加载逻辑 - // 是否有多个useEffect都在加载相同的数据 - // 或者依赖项是否设置正确 - }, []); // 检查依赖项 + const getStatusTag = (status: string) => { + const statusMap: Record = { + SUCCESS: { color: 'success', text: '成功' }, + FAILED: { color: 'error', text: '失败' }, + IN_PROGRESS: { color: 'processing', text: '进行中' }, + HEALTHY: { color: 'success', text: '健康' }, + WARNING: { color: 'warning', text: '警告' }, + ERROR: { color: 'error', text: '错误' } + }; + const statusInfo = statusMap[status] || { color: 'default', text: status }; + return {statusInfo.text}; + }; - return ( -
- - - - } - /> - - - - - } - /> - - - - - } - /> - - - -
- ); + const getResourceUsageStatus = (usage: number) => { + if (usage >= 80) return 'exception'; + if (usage >= 70) return 'warning'; + return 'success'; + }; + + return ( +
+ {/* 部署统计 */} + + + + } + valueStyle={{ color: '#1890ff' }} + /> + + + + + } + /> + + + + + } + valueStyle={{ color: '#1890ff' }} + /> + + + + + } + valueStyle={{ color: '#1890ff' }} + /> + + + + + {/* 部署趋势和环境状态 */} + + + +
+ + + + + + + + + + + +
+
+ + + + ( + +
+
+ {item.name} + {getStatusTag(item.status)} +
+
+ 最后部署: {item.lastDeployment} +
+ `资源使用率 ${percent}%`} + /> +
+
+ )} + /> +
+ +
+ + {/* 最近活动 */} + 查看更多}> + ( + + } />} + title={ + + {item.project} + {item.environment} + {getStatusTag(item.status)} + + } + description={ + + 版本: {item.version} + 操作人: {item.operator} + {item.time} + + } + /> + + )} + /> + +
+ ); }; export default Dashboard; \ No newline at end of file diff --git a/frontend/src/pages/LogStream/index.tsx b/frontend/src/pages/LogStream/index.tsx index f44c9392..153ebfee 100644 --- a/frontend/src/pages/LogStream/index.tsx +++ b/frontend/src/pages/LogStream/index.tsx @@ -1,19 +1,19 @@ import React from 'react'; -import { useParams } from 'react-router-dom'; +import {useParams} from 'react-router-dom'; import LogViewer from './components/LogViewer'; const LogStreamPage: React.FC = () => { - const { processInstanceId } = useParams<{ processInstanceId: string }>(); + const {processInstanceId} = useParams<{ processInstanceId: string }>(); - if (!processInstanceId) { - return
流程实例ID不能为空
; - } + if (!processInstanceId) { + return
流程实例ID不能为空
; + } - return ( -
- -
- ); + return ( +
+ +
+ ); }; export default LogStreamPage; \ No newline at end of file diff --git a/frontend/src/pages/LogStream/service.ts b/frontend/src/pages/LogStream/service.ts index 7e36887b..bb1185c2 100644 --- a/frontend/src/pages/LogStream/service.ts +++ b/frontend/src/pages/LogStream/service.ts @@ -11,7 +11,7 @@ export class LogStreamService { } const token = localStorage.getItem('token'); - const url = new URL(`${window.location.origin}${LOG_STREAM_URL}/dc65ecc5-b6f3-11ef-840c-326a31fc0fe1`); + const url = new URL(`${window.location.origin}${LOG_STREAM_URL}/dfcd8ee0-bc79-11ef-96b6-2ecf01f9c5d5`); if (token) { url.searchParams.append('token', token); diff --git a/frontend/src/pages/System/Menu/service.ts b/frontend/src/pages/System/Menu/service.ts index aa3a72bf..b6ef07bb 100644 --- a/frontend/src/pages/System/Menu/service.ts +++ b/frontend/src/pages/System/Menu/service.ts @@ -126,23 +126,23 @@ export const getCurrentUserMenus = async () => { }; // 添加X6测试菜单 - const x6Test: MenuResponse = { - id: -1, - createTime: new Date().toISOString(), - updateTime: new Date().toISOString(), - version: 0, - name: "X6测试", - path: "/x6-test", - component: "/X6Test/index", - icon: "experiment", - type: MenuTypeEnum.MENU, - parentId: 0, - sort: 1, - hidden: false, - enabled: true, - createBy: "system", - updateBy: "system" - }; + // const x6Test: MenuResponse = { + // id: -1, + // createTime: new Date().toISOString(), + // updateTime: new Date().toISOString(), + // version: 0, + // name: "X6测试", + // path: "/x6-test", + // component: "/X6Test/index", + // icon: "experiment", + // type: MenuTypeEnum.MENU, + // parentId: 0, + // sort: 1, + // hidden: false, + // enabled: true, + // createBy: "system", + // updateBy: "system" + // }; // 处理组件路径格式 const processMenu = (menu: MenuResponse): MenuResponse => { @@ -162,7 +162,7 @@ export const getCurrentUserMenus = async () => { }; const processedMenus = menus.map(processMenu); - const allMenus = [dashboard, x6Test, workflow, ...processedMenus]; + const allMenus = [dashboard, workflow, ...processedMenus]; console.log('All menus:', allMenus); return allMenus; }; diff --git a/frontend/src/pages/Workflow/Instance/components/DetailModal.tsx b/frontend/src/pages/Workflow/Instance/components/DetailModal.tsx index 6aeb1129..e6d0e003 100644 --- a/frontend/src/pages/Workflow/Instance/components/DetailModal.tsx +++ b/frontend/src/pages/Workflow/Instance/components/DetailModal.tsx @@ -96,13 +96,11 @@ const DetailModal: React.FC = ({ visible, onCancel, instanceDa stage.status === 'RUNNING' ? 'blue' : 'gray', children: (
-
{stage.nodeName} ({getNodeTypeText(stage.nodeType)})
-
- 开始:{dayjs(stage.startTime).format('YYYY-MM-DD HH:mm:ss')} -
- {stage.endTime && ( +
{stage.nodeName}
+ {stage.id && (
- 结束:{dayjs(stage.endTime).format('YYYY-MM-DD HH:mm:ss')} + {dayjs(stage.startTime).format('YYYY-MM-DD HH:mm:ss')} + {stage.endTime && ` - ${dayjs(stage.endTime).format('YYYY-MM-DD HH:mm:ss')}`}
)}
{getStatusTag(stage.status)}
diff --git a/frontend/src/pages/Workflow/Instance/index.tsx b/frontend/src/pages/Workflow/Instance/index.tsx index f69b49a0..888dcd17 100644 --- a/frontend/src/pages/Workflow/Instance/index.tsx +++ b/frontend/src/pages/Workflow/Instance/index.tsx @@ -69,9 +69,11 @@ const WorkflowInstanceList: React.FC = () => { render: (status: string) => { if (!status) return '暂无'; const statusMap: Record = { - TERMINATED: { color: 'red', text: '终止' }, - COMPLETED: { color: 'green', text: '已完成' }, - RUNNING: { color: 'blue', text: '运行中' }, + TERMINATED: { color: 'warning', text: '已终止' }, + COMPLETED: { color: 'success', text: '已完成' }, + RUNNING: { color: 'processing', text: '运行中' }, + FAILED: { color: 'error', text: '失败' }, + NOT_STARTED: { color: 'default', text: '未执行' } }; const statusInfo = statusMap[status] || { color: 'default', text: status }; return {statusInfo.text}; @@ -84,7 +86,6 @@ const WorkflowInstanceList: React.FC = () => { width: 200, render: (_, record) => ( - handleViewDetail(record)}>查看详情 handleViewHistory(record)}>历史执行 {record?.lastExecutionStatus === 'RUNNING' && ( console.log('终止流程', record)}>终止流程 diff --git a/frontend/src/pages/Workflow/Monitor/index.tsx b/frontend/src/pages/Workflow/Monitor/index.tsx index 7e8ec2da..541c4a6f 100644 --- a/frontend/src/pages/Workflow/Monitor/index.tsx +++ b/frontend/src/pages/Workflow/Monitor/index.tsx @@ -1,11 +1,194 @@ import React from 'react'; -import {Card} from 'antd'; +import { Card, Row, Col, Statistic, Table, Tag, Space } from 'antd'; +import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend } from 'recharts'; + +// Mock 数据 +const mockStatistics = { + totalInstances: 1256, + runningInstances: 23, + completedToday: 156, + failedToday: 5 +}; + +const mockStatusDistribution = [ + { name: '运行中', value: 23, color: '#1890ff', percent: '1.8%' }, + { name: '已完成', value: 892, color: '#52c41a', percent: '71.0%' }, + { name: '失败', value: 45, color: '#ff4d4f', percent: '3.6%' }, + { name: '已终止', value: 296, color: '#faad14', percent: '23.6%' } +]; + +const mockRecentExecutions = [ + { + id: 1, + name: '数据同步流程', + businessKey: 'SYNC_20241217001', + startTime: '2024-12-17 20:35:18', + status: 'RUNNING', + progress: 45 + }, + { + id: 2, + name: '订单处理流程', + businessKey: 'ORDER_20241217002', + startTime: '2024-12-17 20:30:00', + status: 'COMPLETED', + progress: 100 + }, + { + id: 3, + name: '数据备份流程', + businessKey: 'BACKUP_20241217003', + startTime: '2024-12-17 20:25:00', + status: 'FAILED', + progress: 75 + } +]; const WorkflowMonitor: React.FC = () => { + const getStatusTag = (status: string) => { + const statusMap: Record = { + COMPLETED: { color: 'success', text: '已完成' }, + RUNNING: { color: 'processing', text: '运行中' }, + FAILED: { color: 'error', text: '失败' }, + TERMINATED: { color: 'warning', text: '已终止' } + }; + const statusInfo = statusMap[status] || { color: 'default', text: status }; + return {statusInfo.text}; + }; + + const columns = [ + { + title: '流程名称', + dataIndex: 'name', + key: 'name' + }, + { + title: '业务标识', + dataIndex: 'businessKey', + key: 'businessKey' + }, + { + title: '开始时间', + dataIndex: 'startTime', + key: 'startTime' + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + render: (status: string) => getStatusTag(status) + }, + { + title: '操作', + key: 'action', + render: (_, record: any) => ( + + console.log('查看详情', record)}>查看详情 + {record.status === 'RUNNING' && ( + console.log('终止流程', record)}>终止流程 + )} + + ) + } + ]; + + const CustomTooltip = ({ active, payload }: any) => { + if (active && payload && payload.length) { + const data = payload[0].payload; + return ( +
+

{data.name}

+

数量: {data.value}

+

占比: {data.percent}

+
+ ); + } + return null; + }; + return ( - -
流程监控页面
-
+
+ {/* 统计卡片 */} + + + + + + + + + + + + + + + + + + + + + + + + {/* 状态分布和实时执行 */} + + + +
+ + + `${name} ${(percent * 100).toFixed(1)}%`} + labelLine={true} + > + {mockStatusDistribution.map((entry, index) => ( + + ))} + + } /> + + + +
+
+ + + 查看更多}> + + + + + ); }; diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 07a42440..5ab1beff 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -109,14 +109,14 @@ const router = createBrowserRouter([ } ] }, - { - path: 'x6-test', - element: ( - }> - - - ) - }, + // { + // path: 'x6-test', + // element: ( + // }> + // + // + // ) + // }, { path: 'workflow', children: [