新增: 工程依赖图数据层 + 依赖边渲染 + 小地图
- 后端 module_dependencies 表(V35 迁移)+ ModuleDependencyRepo CRUD - IPC:add/remove/list_module_dependencies - 前端 API 封装 + DependencyGraph 接入真实边数据 - 依赖类型颜色区分(library/api/mq/shared/custom) - 小地图插件(MiniMap)大图概览导航 - 点击节点跳转项目详情 - 任务列表后端真分页 count_by_query 方法
This commit is contained in:
31
bun.lock
31
bun.lock
@@ -5,10 +5,15 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "devflow",
|
"name": "devflow",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@antv/x6": "^3.1.7",
|
||||||
|
"@antv/x6-vue-shape": "^3.0.2",
|
||||||
"@arco-design/web-vue": "^2.58.0",
|
"@arco-design/web-vue": "^2.58.0",
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"dompurify": "^3.4.9",
|
"@tauri-apps/plugin-dialog": "^2.7.1",
|
||||||
|
"dompurify": "^3.4.10",
|
||||||
|
"highlight.js": "^11.11.1",
|
||||||
"marked": "^18.0.5",
|
"marked": "^18.0.5",
|
||||||
|
"tslib": "^2.8.1",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-i18n": "9",
|
"vue-i18n": "9",
|
||||||
"vue-router": "4",
|
"vue-router": "4",
|
||||||
@@ -24,6 +29,10 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@antv/x6": ["@antv/x6@3.1.7", "https://registry.npmmirror.com/@antv/x6/-/x6-3.1.7.tgz", { "dependencies": { "dom-align": "^1.12.4", "lodash-es": "^4.17.15", "mousetrap": "^1.6.5", "utility-types": "^3.10.0" } }, "sha512-NLKXtbCK51oLbazfFD0XsD93rMmih08UBW4gAuEyLBpwAqHmHe+vP8VhOZDkl5O9jV1LSv85IJghr9CT5tZjWw=="],
|
||||||
|
|
||||||
|
"@antv/x6-vue-shape": ["@antv/x6-vue-shape@3.0.2", "https://registry.npmmirror.com/@antv/x6-vue-shape/-/x6-vue-shape-3.0.2.tgz", { "dependencies": { "vue-demi": "latest" }, "peerDependencies": { "@antv/x6": "^3.x", "@vue/composition-api": "^1.0.0-rc.1", "vue": "^2.0.0 || >=3.0.0" }, "optionalPeers": ["@vue/composition-api"] }, "sha512-HWnYFvLCaUD9ernINxTn9vCRj66eG/nXKErSSq0hfI4U1HOitC/1oUrzDdwL/BT80HKAQeSsmy7gpDA/79jYlw=="],
|
||||||
|
|
||||||
"@arco-design/color": ["@arco-design/color@0.4.0", "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz", { "dependencies": { "color": "^3.1.3" } }, "sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g=="],
|
"@arco-design/color": ["@arco-design/color@0.4.0", "https://registry.npmmirror.com/@arco-design/color/-/color-0.4.0.tgz", { "dependencies": { "color": "^3.1.3" } }, "sha512-s7p9MSwJgHeL8DwcATaXvWT3m2SigKpxx4JA1BGPHL4gfvaQsmQfrLBDpjOJFJuJ2jG2dMt3R3P8Pm9E65q18g=="],
|
||||||
|
|
||||||
"@arco-design/web-vue": ["@arco-design/web-vue@2.58.0", "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.58.0.tgz", { "dependencies": { "@arco-design/color": "^0.4.0", "b-tween": "^0.3.3", "b-validate": "^1.5.3", "compute-scroll-into-view": "^1.0.20", "dayjs": "^1.11.13", "number-precision": "^1.6.0", "resize-observer-polyfill": "^1.5.1", "scroll-into-view-if-needed": "^2.2.31", "vue": "^3.1.0" } }, "sha512-b1vdPYOmjG5VAkVa7jlVwCb+WynBK+rnKN8zH3yKohpZObZbostRd3HgYNtjjZjGVU3OqR0Yy2FX7ftgF0bcOw=="],
|
"@arco-design/web-vue": ["@arco-design/web-vue@2.58.0", "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.58.0.tgz", { "dependencies": { "@arco-design/color": "^0.4.0", "b-tween": "^0.3.3", "b-validate": "^1.5.3", "compute-scroll-into-view": "^1.0.20", "dayjs": "^1.11.13", "number-precision": "^1.6.0", "resize-observer-polyfill": "^1.5.1", "scroll-into-view-if-needed": "^2.2.31", "vue": "^3.1.0" } }, "sha512-b1vdPYOmjG5VAkVa7jlVwCb+WynBK+rnKN8zH3yKohpZObZbostRd3HgYNtjjZjGVU3OqR0Yy2FX7ftgF0bcOw=="],
|
||||||
@@ -172,6 +181,8 @@
|
|||||||
|
|
||||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.11.2", "https://registry.npmmirror.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA=="],
|
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.11.2", "https://registry.npmmirror.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA=="],
|
||||||
|
|
||||||
|
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.7.1", "https://registry.npmmirror.com/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.1.tgz", { "dependencies": { "@tauri-apps/api": "^2.11.0" } }, "sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ=="],
|
||||||
|
|
||||||
"@types/dompurify": ["@types/dompurify@3.2.0", "https://registry.npmmirror.com/@types/dompurify/-/dompurify-3.2.0.tgz", { "dependencies": { "dompurify": "*" } }, "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg=="],
|
"@types/dompurify": ["@types/dompurify@3.2.0", "https://registry.npmmirror.com/@types/dompurify/-/dompurify-3.2.0.tgz", { "dependencies": { "dompurify": "*" } }, "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.9", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
|
"@types/estree": ["@types/estree@1.0.9", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
|
||||||
@@ -236,7 +247,9 @@
|
|||||||
|
|
||||||
"de-indent": ["de-indent@1.0.2", "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="],
|
"de-indent": ["de-indent@1.0.2", "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="],
|
||||||
|
|
||||||
"dompurify": ["dompurify@3.4.9", "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.9.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-4dPSRMRDqHvs0V4YDFCsaIZo4if5u0xM+llyxiM2fwuZFdKArUBAF3VtI2+n8NKg9P870WMdYk0UhqQNoWXbfQ=="],
|
"dom-align": ["dom-align@1.12.4", "https://registry.npmmirror.com/dom-align/-/dom-align-1.12.4.tgz", {}, "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw=="],
|
||||||
|
|
||||||
|
"dompurify": ["dompurify@3.4.11", "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.11.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw=="],
|
||||||
|
|
||||||
"entities": ["entities@7.0.1", "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
"entities": ["entities@7.0.1", "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||||
|
|
||||||
@@ -250,14 +263,20 @@
|
|||||||
|
|
||||||
"he": ["he@1.2.0", "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
"he": ["he@1.2.0", "https://registry.npmmirror.com/he/-/he-1.2.0.tgz", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="],
|
||||||
|
|
||||||
|
"highlight.js": ["highlight.js@11.11.1", "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
|
||||||
|
|
||||||
"is-arrayish": ["is-arrayish@0.3.4", "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.4.tgz", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
"is-arrayish": ["is-arrayish@0.3.4", "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.4.tgz", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
||||||
|
|
||||||
|
"lodash-es": ["lodash-es@4.18.1", "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="],
|
||||||
|
|
||||||
"magic-string": ["magic-string@0.30.21", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
"magic-string": ["magic-string@0.30.21", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
|
|
||||||
"marked": ["marked@18.0.5", "https://registry.npmmirror.com/marked/-/marked-18.0.5.tgz", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="],
|
"marked": ["marked@18.0.5", "https://registry.npmmirror.com/marked/-/marked-18.0.5.tgz", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="],
|
||||||
|
|
||||||
"minimatch": ["minimatch@9.0.9", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
"minimatch": ["minimatch@9.0.9", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||||
|
|
||||||
|
"mousetrap": ["mousetrap@1.6.5", "https://registry.npmmirror.com/mousetrap/-/mousetrap-1.6.5.tgz", {}, "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA=="],
|
||||||
|
|
||||||
"muggle-string": ["muggle-string@0.4.1", "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
|
"muggle-string": ["muggle-string@0.4.1", "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.12", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
"nanoid": ["nanoid@3.3.12", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||||
@@ -284,18 +303,26 @@
|
|||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.17", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
"tinyglobby": ["tinyglobby@0.2.17", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
|
||||||
|
|
||||||
|
"tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.6.3", "https://registry.npmmirror.com/typescript/-/typescript-5.6.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
"typescript": ["typescript@5.6.3", "https://registry.npmmirror.com/typescript/-/typescript-5.6.3.tgz", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
||||||
|
|
||||||
|
"utility-types": ["utility-types@3.11.0", "https://registry.npmmirror.com/utility-types/-/utility-types-3.11.0.tgz", {}, "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw=="],
|
||||||
|
|
||||||
"vite": ["vite@6.4.3", "https://registry.npmmirror.com/vite/-/vite-6.4.3.tgz", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="],
|
"vite": ["vite@6.4.3", "https://registry.npmmirror.com/vite/-/vite-6.4.3.tgz", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A=="],
|
||||||
|
|
||||||
"vscode-uri": ["vscode-uri@3.1.0", "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
|
"vscode-uri": ["vscode-uri@3.1.0", "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="],
|
||||||
|
|
||||||
"vue": ["vue@3.5.35", "https://registry.npmmirror.com/vue/-/vue-3.5.35.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.35", "@vue/compiler-sfc": "3.5.35", "@vue/runtime-dom": "3.5.35", "@vue/server-renderer": "3.5.35", "@vue/shared": "3.5.35" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q=="],
|
"vue": ["vue@3.5.35", "https://registry.npmmirror.com/vue/-/vue-3.5.35.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.35", "@vue/compiler-sfc": "3.5.35", "@vue/runtime-dom": "3.5.35", "@vue/server-renderer": "3.5.35", "@vue/shared": "3.5.35" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q=="],
|
||||||
|
|
||||||
|
"vue-demi": ["vue-demi@0.14.10", "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", { "peerDependencies": { "@vue/composition-api": "^1.0.0-rc.1", "vue": "^3.0.0-0 || ^2.6.0" }, "optionalPeers": ["@vue/composition-api"], "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" } }, "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="],
|
||||||
|
|
||||||
"vue-i18n": ["vue-i18n@9.14.5", "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.5.tgz", { "dependencies": { "@intlify/core-base": "9.14.5", "@intlify/shared": "9.14.5", "@vue/devtools-api": "^6.5.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g=="],
|
"vue-i18n": ["vue-i18n@9.14.5", "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.5.tgz", { "dependencies": { "@intlify/core-base": "9.14.5", "@intlify/shared": "9.14.5", "@vue/devtools-api": "^6.5.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g=="],
|
||||||
|
|
||||||
"vue-router": ["vue-router@4.6.4", "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg=="],
|
"vue-router": ["vue-router@4.6.4", "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg=="],
|
||||||
|
|
||||||
"vue-tsc": ["vue-tsc@2.2.12", "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.12.tgz", { "dependencies": { "@volar/typescript": "2.4.15", "@vue/language-core": "2.2.12" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw=="],
|
"vue-tsc": ["vue-tsc@2.2.12", "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.12.tgz", { "dependencies": { "@volar/typescript": "2.4.15", "@vue/language-core": "2.2.12" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw=="],
|
||||||
|
|
||||||
|
"@types/dompurify/dompurify": ["dompurify@3.4.9", "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.9.tgz", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-4dPSRMRDqHvs0V4YDFCsaIZo4if5u0xM+llyxiM2fwuZFdKArUBAF3VtI2+n8NKg9P870WMdYk0UhqQNoWXbfQ=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ mod project_service_repo;
|
|||||||
mod settings;
|
mod settings;
|
||||||
mod task_link_repo;
|
mod task_link_repo;
|
||||||
mod task_repo;
|
mod task_repo;
|
||||||
|
mod module_dependency_repo;
|
||||||
|
|
||||||
pub use conversation_repo::*;
|
pub use conversation_repo::*;
|
||||||
pub use idea_eval_repo::*;
|
pub use idea_eval_repo::*;
|
||||||
@@ -40,6 +41,7 @@ pub use project_service_repo::*;
|
|||||||
pub use settings::*;
|
pub use settings::*;
|
||||||
pub use task_link_repo::*;
|
pub use task_link_repo::*;
|
||||||
pub use task_repo::*;
|
pub use task_repo::*;
|
||||||
|
pub use module_dependency_repo::*;
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 辅助宏 — 消除多个 Repo 的重复样板
|
// 辅助宏 — 消除多个 Repo 的重复样板
|
||||||
|
|||||||
95
crates/df-storage/src/crud/module_dependency_repo.rs
Normal file
95
crates/df-storage/src/crud/module_dependency_repo.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
//! 工程依赖关系 — module_dependencies 表 CRUD(V35,工程间依赖边)
|
||||||
|
//!
|
||||||
|
//! 简化版:insert / delete / list_by_field(查 project_id)。
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rusqlite::{params, OptionalExtension, Row};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::db::Database;
|
||||||
|
use crate::models::ModuleDependencyRecord;
|
||||||
|
|
||||||
|
use super::{storage_err};
|
||||||
|
|
||||||
|
fn dep_from_row(row: &Row<'_>) -> std::result::Result<ModuleDependencyRecord, rusqlite::Error> {
|
||||||
|
Ok(ModuleDependencyRecord {
|
||||||
|
id: row.get("id")?,
|
||||||
|
project_id: row.get("project_id")?,
|
||||||
|
from_module_id: row.get("from_module_id")?,
|
||||||
|
to_module_id: row.get("to_module_id")?,
|
||||||
|
dep_type: row.get("dep_type")?,
|
||||||
|
label: row.get("label")?,
|
||||||
|
created_at: row.get("created_at")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ModuleDependencyRepo {
|
||||||
|
conn: Arc<Mutex<rusqlite::Connection>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleDependencyRepo {
|
||||||
|
pub fn new(db: &Database) -> Self {
|
||||||
|
Self { conn: db.conn() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn insert(&self, record: ModuleDependencyRecord) -> Result<bool, df_types::error::Error> {
|
||||||
|
let conn = self.conn.clone();
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let guard = conn.blocking_lock();
|
||||||
|
let r = &record;
|
||||||
|
let affected = guard
|
||||||
|
.execute(
|
||||||
|
"INSERT INTO module_dependencies \
|
||||||
|
(id, project_id, from_module_id, to_module_id, dep_type, label, created_at) \
|
||||||
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||||||
|
params![
|
||||||
|
r.id, r.project_id, r.from_module_id, r.to_module_id,
|
||||||
|
r.dep_type, r.label, r.created_at,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.map_err(storage_err)?;
|
||||||
|
Ok(affected > 0)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(storage_err)?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete(&self, id: &str) -> Result<bool, df_types::error::Error> {
|
||||||
|
let conn = self.conn.clone();
|
||||||
|
let id = id.to_owned();
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let guard = conn.blocking_lock();
|
||||||
|
let affected = guard
|
||||||
|
.execute("DELETE FROM module_dependencies WHERE id = ?1", params![id])
|
||||||
|
.map_err(storage_err)?;
|
||||||
|
Ok(affected > 0)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(storage_err)?
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_by_field(&self, field: &str, value: &str) -> Result<Vec<ModuleDependencyRecord>, df_types::error::Error> {
|
||||||
|
let conn = self.conn.clone();
|
||||||
|
let field = field.to_owned();
|
||||||
|
let value = value.to_owned();
|
||||||
|
tokio::task::spawn_blocking(move || {
|
||||||
|
let guard = conn.blocking_lock();
|
||||||
|
let sql = format!(
|
||||||
|
"SELECT id, project_id, from_module_id, to_module_id, dep_type, label, created_at \
|
||||||
|
FROM module_dependencies WHERE {field} = ?1 ORDER BY created_at ASC"
|
||||||
|
);
|
||||||
|
let mut stmt = guard.prepare(&sql).map_err(storage_err)?;
|
||||||
|
let rows = stmt
|
||||||
|
.query_map(params![value], dep_from_row)
|
||||||
|
.map_err(storage_err)?;
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for r in rows {
|
||||||
|
results.push(r.map_err(storage_err)?);
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(storage_err)?
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ pub fn run(conn: &Connection) -> Result<()> {
|
|||||||
// 什么数据库、Redis 在哪、有没有 MQ"的基础设施上下文。
|
// 什么数据库、Redis 在哪、有没有 MQ"的基础设施上下文。
|
||||||
// V33 = 审批重启恢复:ai_conversations 加 pending_approvals TEXT 列,持久化挂起审批快照,
|
// V33 = 审批重启恢复:ai_conversations 加 pending_approvals TEXT 列,持久化挂起审批快照,
|
||||||
// 重启后从 DB 恢复 pending_approvals 内存态,使待审批不丢。
|
// 重启后从 DB 恢复 pending_approvals 内存态,使待审批不丢。
|
||||||
let steps: [(i32, fn(&Connection) -> Result<()>); 34] = [
|
let steps: [(i32, fn(&Connection) -> Result<()>); 35] = [
|
||||||
(1, migrate_v1),
|
(1, migrate_v1),
|
||||||
(2, migrate_v2),
|
(2, migrate_v2),
|
||||||
(3, migrate_v3),
|
(3, migrate_v3),
|
||||||
@@ -80,6 +80,7 @@ pub fn run(conn: &Connection) -> Result<()> {
|
|||||||
(32, migrate_v32),
|
(32, migrate_v32),
|
||||||
(33, migrate_v33),
|
(33, migrate_v33),
|
||||||
(34, migrate_v34),
|
(34, migrate_v34),
|
||||||
|
(35, migrate_v35),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (version, migrate_fn) in steps {
|
for (version, migrate_fn) in steps {
|
||||||
@@ -995,6 +996,40 @@ fn migrate_v34(conn: &Connection) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// V35:工程依赖关系—module_dependencies 表(工程间依赖边,用于依赖图)
|
||||||
|
///
|
||||||
|
/// dep_type 枚举值:library(类库) / api(API调用) / mq(消息队列) / shared(共享资源) / custom(自定义)
|
||||||
|
fn migrate_v35(conn: &Connection) -> Result<()> {
|
||||||
|
conn.execute(
|
||||||
|
"CREATE TABLE IF NOT EXISTS module_dependencies (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
project_id TEXT NOT NULL REFERENCES projects(id),
|
||||||
|
from_module_id TEXT NOT NULL REFERENCES project_modules(id),
|
||||||
|
to_module_id TEXT NOT NULL REFERENCES project_modules(id),
|
||||||
|
dep_type TEXT NOT NULL DEFAULT 'library',
|
||||||
|
label TEXT,
|
||||||
|
created_at TEXT NOT NULL
|
||||||
|
)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
conn.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_module_deps_project ON module_dependencies(project_id)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
conn.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_module_deps_from ON module_dependencies(from_module_id)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
conn.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_module_deps_to ON module_dependencies(to_module_id)",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
tracing::info!("v35: 建 module_dependencies 表 + 索引(工程依赖图)");
|
||||||
|
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [35])?;
|
||||||
|
tracing::info!("迁移 v35 完成");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
|
/// V21 建表 SQL — 消息拆分存储 ai_messages 表
|
||||||
///
|
///
|
||||||
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。
|
/// 与 V9_SQL 中的 ai_messages 镜像(V9 给新库,此 const 给老库 V21 迁移用 IF NOT EXISTS)。
|
||||||
|
|||||||
@@ -483,3 +483,17 @@ pub struct ProjectModuleRecord {
|
|||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 工程依赖关系记录(V35 module_dependencies 表)。
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||||
|
pub struct ModuleDependencyRecord {
|
||||||
|
pub id: String,
|
||||||
|
pub project_id: String,
|
||||||
|
pub from_module_id: String,
|
||||||
|
pub to_module_id: String,
|
||||||
|
/// 依赖类型:library / api / mq / shared / custom
|
||||||
|
pub dep_type: String,
|
||||||
|
/// 可选标签(自定义描述)
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub created_at: String,
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use std::process::Command;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tauri::State;
|
use tauri::State;
|
||||||
|
|
||||||
use df_storage::models::ProjectModuleRecord;
|
use df_storage::models::{ModuleDependencyRecord, ProjectModuleRecord};
|
||||||
use df_types::types::new_id;
|
use df_types::types::new_id;
|
||||||
|
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
@@ -1133,3 +1133,68 @@ pub async fn get_commit_detail(
|
|||||||
"full_message": full_message,
|
"full_message": full_message,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// IPC 命令 — 工程依赖关系(module_dependencies 表)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/// 新增工程依赖。返回完整记录。
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn add_module_dependency(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
project_id: String,
|
||||||
|
from_module_id: String,
|
||||||
|
to_module_id: String,
|
||||||
|
dep_type: Option<String>,
|
||||||
|
label: Option<String>,
|
||||||
|
) -> Result<ModuleDependencyRecord, String> {
|
||||||
|
let project_id = project_id.trim().to_string();
|
||||||
|
let from_module_id = from_module_id.trim().to_string();
|
||||||
|
let to_module_id = to_module_id.trim().to_string();
|
||||||
|
if project_id.is_empty() || from_module_id.is_empty() || to_module_id.is_empty() {
|
||||||
|
return Err("project_id / from_module_id / to_module_id 不能为空".to_string());
|
||||||
|
}
|
||||||
|
let dep_type = dep_type.unwrap_or_else(|| "library".to_string());
|
||||||
|
let record = ModuleDependencyRecord {
|
||||||
|
id: new_id(),
|
||||||
|
project_id,
|
||||||
|
from_module_id,
|
||||||
|
to_module_id,
|
||||||
|
dep_type,
|
||||||
|
label: trim_opt(label),
|
||||||
|
created_at: now_millis(),
|
||||||
|
};
|
||||||
|
state.module_dependencies.insert(record.clone()).await.map_err(err_str)?;
|
||||||
|
Ok(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除工程依赖。
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn remove_module_dependency(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
id: String,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let id = id.trim().to_string();
|
||||||
|
if id.is_empty() {
|
||||||
|
return Err("id 不能为空".to_string());
|
||||||
|
}
|
||||||
|
state.module_dependencies.delete(&id).await.map_err(err_str)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 列出项目的全部工程依赖。
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn list_module_dependencies(
|
||||||
|
state: State<'_, AppState>,
|
||||||
|
project_id: String,
|
||||||
|
) -> Result<Vec<ModuleDependencyRecord>, String> {
|
||||||
|
let project_id = project_id.trim().to_string();
|
||||||
|
if project_id.is_empty() {
|
||||||
|
return Err("project_id 不能为空".to_string());
|
||||||
|
}
|
||||||
|
state
|
||||||
|
.module_dependencies
|
||||||
|
.list_by_field("project_id", &project_id)
|
||||||
|
.await
|
||||||
|
.map_err(err_str)
|
||||||
|
}
|
||||||
|
|||||||
@@ -334,6 +334,9 @@ pub fn run() {
|
|||||||
commands::module::get_module_file_diff,
|
commands::module::get_module_file_diff,
|
||||||
commands::module::get_module_commits,
|
commands::module::get_module_commits,
|
||||||
commands::module::get_commit_detail,
|
commands::module::get_commit_detail,
|
||||||
|
commands::module::add_module_dependency,
|
||||||
|
commands::module::remove_module_dependency,
|
||||||
|
commands::module::list_module_dependencies,
|
||||||
// 灵感
|
// 灵感
|
||||||
commands::idea::list_ideas,
|
commands::idea::list_ideas,
|
||||||
commands::idea::create_idea,
|
commands::idea::create_idea,
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ use df_storage::crud::{
|
|||||||
AiConversationRepo, AiMessageRepo, AiProviderRepo, AiToolExecutionRepo, IdeaEvalRepo, IdeaRepo,
|
AiConversationRepo, AiMessageRepo, AiProviderRepo, AiToolExecutionRepo, IdeaEvalRepo, IdeaRepo,
|
||||||
KnowledgeEventsRepo, KnowledgeRepo, NodeExecutionRepo, ProjectEventRepo, ProjectModuleRepo,
|
KnowledgeEventsRepo, KnowledgeRepo, NodeExecutionRepo, ProjectEventRepo, ProjectModuleRepo,
|
||||||
ProjectRepo, ProjectServiceRepo, ReleaseRepo, SettingsRepo, TaskLinkRepo, TaskRepo,
|
ProjectRepo, ProjectServiceRepo, ReleaseRepo, SettingsRepo, TaskLinkRepo, TaskRepo,
|
||||||
WorkflowRepo,
|
WorkflowRepo, ModuleDependencyRepo,
|
||||||
};
|
};
|
||||||
use df_storage::db::Database;
|
use df_storage::db::Database;
|
||||||
use df_workflow::eventbus::EventBus;
|
use df_workflow::eventbus::EventBus;
|
||||||
@@ -75,6 +75,8 @@ pub struct AppState {
|
|||||||
/// 项目多工程(每个工程独立代码仓库):Monorepo 多仓库 / 微服务 / 前后端分离。
|
/// 项目多工程(每个工程独立代码仓库):Monorepo 多仓库 / 微服务 / 前后端分离。
|
||||||
/// 记录工程元数据(路径/Git 地址/技术栈);Git 状态(分支/改动/提交)实时派生不存表。
|
/// 记录工程元数据(路径/Git 地址/技术栈);Git 状态(分支/改动/提交)实时派生不存表。
|
||||||
pub project_modules: ProjectModuleRepo,
|
pub project_modules: ProjectModuleRepo,
|
||||||
|
/// 工程依赖关系 Repo(V35 module_dependencies,依赖图边数据)。
|
||||||
|
pub module_dependencies: ModuleDependencyRepo,
|
||||||
/// 发布表 Repo(预留:ReleaseRepo 持久化已就位·IPC/逻辑未接入·SW-260618-21 b 保留)
|
/// 发布表 Repo(预留:ReleaseRepo 持久化已就位·IPC/逻辑未接入·SW-260618-21 b 保留)
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub releases: ReleaseRepo,
|
pub releases: ReleaseRepo,
|
||||||
@@ -209,6 +211,7 @@ impl AppState {
|
|||||||
project_events: ProjectEventRepo::new(&db),
|
project_events: ProjectEventRepo::new(&db),
|
||||||
project_services: ProjectServiceRepo::new(&db),
|
project_services: ProjectServiceRepo::new(&db),
|
||||||
project_modules: ProjectModuleRepo::new(&db),
|
project_modules: ProjectModuleRepo::new(&db),
|
||||||
|
module_dependencies: ModuleDependencyRepo::new(&db),
|
||||||
releases: ReleaseRepo::new(&db),
|
releases: ReleaseRepo::new(&db),
|
||||||
workflows: WorkflowRepo::new(&db),
|
workflows: WorkflowRepo::new(&db),
|
||||||
node_executions: NodeExecutionRepo::new(&db),
|
node_executions: NodeExecutionRepo::new(&db),
|
||||||
|
|||||||
@@ -59,6 +59,17 @@ export interface GitChangedFile {
|
|||||||
path: string
|
path: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 工程依赖关系记录(对齐后端 ModuleDependencyRecord)。 */
|
||||||
|
export interface ModuleDependencyRecord {
|
||||||
|
id: string
|
||||||
|
project_id: string
|
||||||
|
from_module_id: string
|
||||||
|
to_module_id: string
|
||||||
|
dep_type: string
|
||||||
|
label: string | null
|
||||||
|
created_at: string
|
||||||
|
}
|
||||||
|
|
||||||
/** 最近提交项(对齐后端 GitRecentCommit)。 */
|
/** 最近提交项(对齐后端 GitRecentCommit)。 */
|
||||||
export interface GitRecentCommit {
|
export interface GitRecentCommit {
|
||||||
hash: string
|
hash: string
|
||||||
@@ -157,6 +168,21 @@ export const moduleApi = {
|
|||||||
return invoke('list_branches', { moduleId })
|
return invoke('list_branches', { moduleId })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** 列出项目全部工程依赖。 */
|
||||||
|
listModuleDependencies(projectId: string): Promise<ModuleDependencyRecord[]> {
|
||||||
|
return invoke('list_module_dependencies', { projectId })
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 新增工程依赖。 */
|
||||||
|
addModuleDependency(projectId: string, fromModuleId: string, toModuleId: string, depType?: string, label?: string): Promise<ModuleDependencyRecord> {
|
||||||
|
return invoke('add_module_dependency', { projectId, fromModuleId, toModuleId, depType: depType ?? null, label: label ?? null })
|
||||||
|
},
|
||||||
|
|
||||||
|
/** 删除工程依赖。 */
|
||||||
|
removeModuleDependency(id: string): Promise<void> {
|
||||||
|
return invoke('remove_module_dependency', { id })
|
||||||
|
},
|
||||||
|
|
||||||
getModuleCommits(moduleId: string, skip?: number, limit?: number): Promise<{
|
getModuleCommits(moduleId: string, skip?: number, limit?: number): Promise<{
|
||||||
commits: { hash: string; subject: string; timestamp: number; author: string }[]
|
commits: { hash: string; subject: string; timestamp: number; author: string }[]
|
||||||
has_more: boolean
|
has_more: boolean
|
||||||
|
|||||||
@@ -23,30 +23,40 @@
|
|||||||
* 节点用 Vue 组件(ModuleNode.vue)渲染,通过 @antv/x6-vue-shape 注册。
|
* 节点用 Vue 组件(ModuleNode.vue)渲染,通过 @antv/x6-vue-shape 注册。
|
||||||
*/
|
*/
|
||||||
import { onMounted, onBeforeUnmount, ref, watch, markRaw } from 'vue'
|
import { onMounted, onBeforeUnmount, ref, watch, markRaw } from 'vue'
|
||||||
import { Graph, Selection, Snapline, History, Scroller } from '@antv/x6'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { Graph, Selection, Snapline, History, Scroller, MiniMap } from '@antv/x6'
|
||||||
import '@antv/x6-vue-shape'
|
import '@antv/x6-vue-shape'
|
||||||
import { moduleApi, type ProjectModuleRecord } from '@/api/module'
|
import { moduleApi, type ProjectModuleRecord, type ModuleDependencyRecord } from '@/api/module'
|
||||||
import ModuleNode from './ModuleNode.vue'
|
import ModuleNode from './ModuleNode.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
projectId: string
|
projectId: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'select-module', moduleId: string): void
|
(e: 'select-module', moduleId: string): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const containerRef = ref<HTMLElement | null>(null)
|
const containerRef = ref<HTMLElement | null>(null)
|
||||||
const modules = ref<ProjectModuleRecord[]>([])
|
const modules = ref<ProjectModuleRecord[]>([])
|
||||||
|
const dependencies = ref<ModuleDependencyRecord[]>([])
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
let graph: Graph | null = null
|
let graph: Graph | null = null
|
||||||
|
|
||||||
async function loadModules() {
|
async function loadModules() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
modules.value = await moduleApi.listProjectModules(props.projectId)
|
const [mods, deps] = await Promise.all([
|
||||||
|
moduleApi.listProjectModules(props.projectId),
|
||||||
|
moduleApi.listModuleDependencies(props.projectId),
|
||||||
|
])
|
||||||
|
modules.value = mods
|
||||||
|
dependencies.value = deps
|
||||||
} catch {
|
} catch {
|
||||||
modules.value = []
|
modules.value = []
|
||||||
|
dependencies.value = []
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -81,8 +91,21 @@ function renderGraph() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 边暂为空(module_dependencies 表未建)
|
// 边数据(module_dependencies 表)
|
||||||
graph.fromJSON({ nodes, edges: [] })
|
const edges = dependencies.value.map(d => ({
|
||||||
|
source: d.from_module_id,
|
||||||
|
target: d.to_module_id,
|
||||||
|
labels: d.label ? [{ text: d.label }] : [{ text: d.dep_type }],
|
||||||
|
attrs: {
|
||||||
|
line: {
|
||||||
|
stroke: depTypeColor(d.dep_type),
|
||||||
|
strokeWidth: 1.5,
|
||||||
|
targetMarker: { name: 'block', width: 8, height: 6 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
graph.fromJSON({ nodes, edges })
|
||||||
graph.centerContent()
|
graph.centerContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +124,11 @@ function buildGraph() {
|
|||||||
graph.use(new Snapline({ enabled: true }))
|
graph.use(new Snapline({ enabled: true }))
|
||||||
graph.use(new History({ enabled: true }))
|
graph.use(new History({ enabled: true }))
|
||||||
graph.use(new Scroller({ enabled: true, pannable: true }))
|
graph.use(new Scroller({ enabled: true, pannable: true }))
|
||||||
|
graph.use(new MiniMap({ width: 200, height: 120, padding: 10 }))
|
||||||
|
|
||||||
graph.on('node:click', ({ node }) => {
|
graph.on('node:click', ({ node }) => {
|
||||||
emit('select-module', String(node.id))
|
emit('select-module', String(node.id))
|
||||||
|
router.push(`/projects/${props.projectId}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
renderGraph()
|
renderGraph()
|
||||||
@@ -121,6 +146,17 @@ function zoomOut() {
|
|||||||
graph?.zoom(-0.1)
|
graph?.zoom(-0.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 依赖类型 → 边颜色 */
|
||||||
|
function depTypeColor(depType: string): string {
|
||||||
|
switch (depType) {
|
||||||
|
case 'api': return '#5378e8'
|
||||||
|
case 'mq': return '#e8a053'
|
||||||
|
case 'shared': return '#53e8a0'
|
||||||
|
case 'custom': return '#e853a0'
|
||||||
|
default: return '#5c6b8a' // library
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await loadModules()
|
await loadModules()
|
||||||
buildGraph()
|
buildGraph()
|
||||||
|
|||||||
Reference in New Issue
Block a user