1
This commit is contained in:
parent
bfb7c7b6da
commit
fa4b146693
285
frontend/package-lock.json
generated
285
frontend/package-lock.json
generated
@ -24,6 +24,10 @@
|
||||
"@logicflow/core": "^2.0.9",
|
||||
"@logicflow/extension": "^2.0.13",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@rjsf/antd": "^5.23.2",
|
||||
"@rjsf/core": "^5.23.2",
|
||||
"@rjsf/utils": "^5.23.2",
|
||||
"@rjsf/validator-ajv8": "^5.23.2",
|
||||
"@types/recharts": "^1.8.29",
|
||||
"ajv": "^8.17.1",
|
||||
"ajv-formats": "^3.0.1",
|
||||
@ -1948,6 +1952,163 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/antd": {
|
||||
"version": "5.23.2",
|
||||
"resolved": "https://registry.npmmirror.com/@rjsf/antd/-/antd-5.23.2.tgz",
|
||||
"integrity": "sha512-XZG0sIiJTjxdNXAIFyM88nP7MmCLUxugm3wyJ34jQlwGThZqmsgbHqyhrTum9ZcOvMAknDeNK3MSwI0CugswtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"classnames": "^2.5.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"rc-picker": "2.7.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ant-design/icons": "^4.0.0 || ^5.0.0",
|
||||
"@rjsf/core": "^5.23.x",
|
||||
"@rjsf/utils": "^5.23.x",
|
||||
"antd": "^4.24.0 || ^5.8.5",
|
||||
"dayjs": "^1.8.0",
|
||||
"react": "^16.14.0 || >=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/antd/node_modules/rc-picker": {
|
||||
"version": "2.7.6",
|
||||
"resolved": "https://registry.npmmirror.com/rc-picker/-/rc-picker-2.7.6.tgz",
|
||||
"integrity": "sha512-H9if/BUJUZBOhPfWcPeT15JUI3/ntrG9muzERrXDkSoWmDj4yzmBvumozpxYrHwjcKnjyDGAke68d+whWwvhHA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "^2.2.1",
|
||||
"date-fns": "2.x",
|
||||
"dayjs": "1.x",
|
||||
"moment": "^2.24.0",
|
||||
"rc-trigger": "^5.0.4",
|
||||
"rc-util": "^5.37.0",
|
||||
"shallowequal": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.x"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0",
|
||||
"react-dom": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/antd/node_modules/rc-picker/node_modules/rc-trigger": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmmirror.com/rc-trigger/-/rc-trigger-5.3.4.tgz",
|
||||
"integrity": "sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"classnames": "^2.2.6",
|
||||
"rc-align": "^4.0.0",
|
||||
"rc-motion": "^2.0.0",
|
||||
"rc-util": "^5.19.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.x"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0",
|
||||
"react-dom": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/antd/node_modules/rc-picker/node_modules/rc-trigger/node_modules/rc-align": {
|
||||
"version": "4.0.15",
|
||||
"resolved": "https://registry.npmmirror.com/rc-align/-/rc-align-4.0.15.tgz",
|
||||
"integrity": "sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
"classnames": "2.x",
|
||||
"dom-align": "^1.7.0",
|
||||
"rc-util": "^5.26.0",
|
||||
"resize-observer-polyfill": "^1.5.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.9.0",
|
||||
"react-dom": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/core": {
|
||||
"version": "5.23.2",
|
||||
"resolved": "https://registry.npmmirror.com/@rjsf/core/-/core-5.23.2.tgz",
|
||||
"integrity": "sha512-jSz3X8SOZ1xL3wMPhBt01j/cA4FszhdGTLBhHfncM9nme4SpbQ6GRAiePOBakEVhT89GAM11ML1DA888YTZfmg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"markdown-to-jsx": "^7.4.1",
|
||||
"nanoid": "^3.3.7",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@rjsf/utils": "^5.23.x",
|
||||
"react": "^16.14.0 || >=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/utils": {
|
||||
"version": "5.23.2",
|
||||
"resolved": "https://registry.npmmirror.com/@rjsf/utils/-/utils-5.23.2.tgz",
|
||||
"integrity": "sha512-CDpZTroRE1O2lkZBNYsWCRj7moF3fqnwIo/Vf3flhoS7rGwb8kWlIHakOOBZxlqorKZB1UPzhQZn+FcwhzNldw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"json-schema-merge-allof": "^0.8.1",
|
||||
"jsonpointer": "^5.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react-is": "^18.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || >=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/validator-ajv8": {
|
||||
"version": "5.23.2",
|
||||
"resolved": "https://registry.npmmirror.com/@rjsf/validator-ajv8/-/validator-ajv8-5.23.2.tgz",
|
||||
"integrity": "sha512-EHOTZ/YxTcFHpCj2p42PD3daxJyozZ5gkIVCfbtIXgTtubt/pOMFvM2OxNcQ6OBQ+ulo96OmnDxYGsNPpZZVaw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ajv": "^8.12.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@rjsf/utils": "^5.23.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@rjsf/validator-ajv8/node_modules/ajv-formats": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz",
|
||||
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ajv": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ajv": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.27.4",
|
||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz",
|
||||
@ -3224,6 +3385,27 @@
|
||||
"resolved": "https://registry.npmmirror.com/component-indexof/-/component-indexof-0.0.3.tgz",
|
||||
"integrity": "sha512-puDQKvx/64HZXb4hBwIcvQLaLgux8o1CbWl39s41hrIIZDl1lJiD5jc22gj3RBeGK0ovxALDYpIbyjqDUUl0rw=="
|
||||
},
|
||||
"node_modules/compute-gcd": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/compute-gcd/-/compute-gcd-1.2.1.tgz",
|
||||
"integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==",
|
||||
"dependencies": {
|
||||
"validate.io-array": "^1.0.3",
|
||||
"validate.io-function": "^1.0.2",
|
||||
"validate.io-integer-array": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compute-lcm": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/compute-lcm/-/compute-lcm-1.1.2.tgz",
|
||||
"integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==",
|
||||
"dependencies": {
|
||||
"compute-gcd": "^1.2.1",
|
||||
"validate.io-array": "^1.0.3",
|
||||
"validate.io-function": "^1.0.2",
|
||||
"validate.io-integer-array": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/compute-scroll-into-view": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz",
|
||||
@ -3495,6 +3677,22 @@
|
||||
"lodash": "^4.17.15"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "2.30.0",
|
||||
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz",
|
||||
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.11"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/date-fns"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
|
||||
@ -4608,6 +4806,29 @@
|
||||
"resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||
},
|
||||
"node_modules/json-schema-compare": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/json-schema-compare/-/json-schema-compare-0.2.2.tgz",
|
||||
"integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.4"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-merge-allof": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz",
|
||||
"integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"compute-lcm": "^1.1.2",
|
||||
"json-schema-compare": "^0.2.2",
|
||||
"lodash": "^4.17.20"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||
@ -4638,6 +4859,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonpointer": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/jsonpointer/-/jsonpointer-5.0.1.tgz",
|
||||
"integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
|
||||
@ -4790,6 +5020,18 @@
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-to-jsx": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmmirror.com/markdown-to-jsx/-/markdown-to-jsx-7.7.2.tgz",
|
||||
"integrity": "sha512-N3AKfYRvxNscvcIH6HDnDKILp4S8UWbebp+s92Y8SwIq0CuSbLW4Jgmrbjku3CWKjTQO0OyIMS6AhzqrwjEa3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/medium-editor": {
|
||||
"version": "5.23.3",
|
||||
"resolved": "https://registry.npmmirror.com/medium-editor/-/medium-editor-5.23.3.tgz",
|
||||
@ -4940,6 +5182,15 @@
|
||||
"mobx": "^4.13.1 || ^5.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/mousetrap": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmmirror.com/mousetrap/-/mousetrap-1.6.5.tgz",
|
||||
@ -4954,7 +5205,6 @@
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -6817,6 +7067,39 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/validate.io-array": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/validate.io-array/-/validate.io-array-1.0.6.tgz",
|
||||
"integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/validate.io-function": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/validate.io-function/-/validate.io-function-1.0.2.tgz",
|
||||
"integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ=="
|
||||
},
|
||||
"node_modules/validate.io-integer": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/validate.io-integer/-/validate.io-integer-1.0.5.tgz",
|
||||
"integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==",
|
||||
"dependencies": {
|
||||
"validate.io-number": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/validate.io-integer-array": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz",
|
||||
"integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==",
|
||||
"dependencies": {
|
||||
"validate.io-array": "^1.0.3",
|
||||
"validate.io-integer": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/validate.io-number": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/validate.io-number/-/validate.io-number-1.0.3.tgz",
|
||||
"integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg=="
|
||||
},
|
||||
"node_modules/vanilla-picker": {
|
||||
"version": "2.12.3",
|
||||
"resolved": "https://registry.npmmirror.com/vanilla-picker/-/vanilla-picker-2.12.3.tgz",
|
||||
|
||||
@ -26,6 +26,10 @@
|
||||
"@logicflow/core": "^2.0.9",
|
||||
"@logicflow/extension": "^2.0.13",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@rjsf/antd": "^5.23.2",
|
||||
"@rjsf/core": "^5.23.2",
|
||||
"@rjsf/utils": "^5.23.2",
|
||||
"@rjsf/validator-ajv8": "^5.23.2",
|
||||
"@types/recharts": "^1.8.29",
|
||||
"ajv": "^8.17.1",
|
||||
"ajv-formats": "^3.0.1",
|
||||
|
||||
@ -0,0 +1,205 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Modal, Form, Select, Switch, InputNumber, message} from 'antd';
|
||||
import type {DeploymentConfig, DeployConfigTemplate} from '../types';
|
||||
import {createDeploymentConfig, updateDeploymentConfig, getDeployConfigTemplates} from '../service';
|
||||
import {getApplicationList} from '../../../Application/List/service';
|
||||
import type {Application} from '../../../Application/List/types';
|
||||
import {withTheme} from '@rjsf/core';
|
||||
import {Theme as AntdTheme} from '@rjsf/antd';
|
||||
import validator from '@rjsf/validator-ajv8';
|
||||
import type {IChangeEvent} from '@rjsf/utils';
|
||||
|
||||
const JsonForm = withTheme(AntdTheme);
|
||||
|
||||
interface DeploymentConfigModalProps {
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
initialValues?: DeploymentConfig;
|
||||
envId: number;
|
||||
}
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
const DeploymentConfigModal: React.FC<DeploymentConfigModalProps> = ({
|
||||
visible,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
initialValues,
|
||||
envId,
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [applications, setApplications] = useState<Application[]>([]);
|
||||
const [templates, setTemplates] = useState<DeployConfigTemplate[]>([]);
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<DeployConfigTemplate>();
|
||||
const [buildVariables, setBuildVariables] = useState<Record<string, any>>({});
|
||||
const isEdit = !!initialValues?.id;
|
||||
|
||||
// 获取应用列表
|
||||
const fetchApplications = async () => {
|
||||
try {
|
||||
const data = await getApplicationList();
|
||||
setApplications(data);
|
||||
} catch (error) {
|
||||
message.error('获取应用列表失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 获取配置模板
|
||||
const fetchTemplates = async () => {
|
||||
try {
|
||||
const data = await getDeployConfigTemplates();
|
||||
setTemplates(data);
|
||||
} catch (error) {
|
||||
message.error('获取配置模板失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 仅在模态框显示时获取数据
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
fetchApplications();
|
||||
fetchTemplates();
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
// 初始化表单数据
|
||||
useEffect(() => {
|
||||
if (!visible) return;
|
||||
|
||||
if (initialValues) {
|
||||
form.setFieldsValue(initialValues);
|
||||
const template = templates.find(t => t.code === initialValues.templateCode);
|
||||
if (template) {
|
||||
setSelectedTemplate(template);
|
||||
setBuildVariables(initialValues.buildVariables || {});
|
||||
}
|
||||
} else {
|
||||
form.resetFields();
|
||||
form.setFieldsValue({
|
||||
envId,
|
||||
enabled: true,
|
||||
sort: 0,
|
||||
});
|
||||
setSelectedTemplate(undefined);
|
||||
setBuildVariables({});
|
||||
}
|
||||
}, [visible, initialValues, envId, form]);
|
||||
|
||||
const handleAppChange = (appId: number) => {
|
||||
const app = applications.find(a => a.id === appId);
|
||||
if (app) {
|
||||
const template = templates.find(t => t.languageType === app.language);
|
||||
setSelectedTemplate(template);
|
||||
setBuildVariables({});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
const submitData = {
|
||||
...values,
|
||||
templateCode: selectedTemplate?.code,
|
||||
buildVariables,
|
||||
};
|
||||
|
||||
if (isEdit) {
|
||||
await updateDeploymentConfig({
|
||||
...submitData,
|
||||
id: initialValues.id,
|
||||
});
|
||||
} else {
|
||||
await createDeploymentConfig(submitData);
|
||||
}
|
||||
message.success(`${isEdit ? '更新' : '创建'}成功`);
|
||||
form.resetFields();
|
||||
onSuccess();
|
||||
} catch (error) {
|
||||
message.error(`${isEdit ? '更新' : '创建'}失败`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleJsonFormChange = (e: IChangeEvent) => {
|
||||
if (e.formData) {
|
||||
setBuildVariables(e.formData);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={`${isEdit ? '编辑' : '新建'}部署配置`}
|
||||
open={visible}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
onCancel();
|
||||
}}
|
||||
onOk={handleSubmit}
|
||||
width={800}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
enabled: true,
|
||||
sort: 0,
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="appId"
|
||||
label="应用"
|
||||
rules={[{required: true, message: '请选择应用'}]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择应用"
|
||||
onChange={handleAppChange}
|
||||
disabled={isEdit}
|
||||
>
|
||||
{applications.map((app) => (
|
||||
<Option key={app.id} value={app.id}>
|
||||
{app.appName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{selectedTemplate && (
|
||||
<Form.Item
|
||||
label={selectedTemplate.name}
|
||||
required
|
||||
>
|
||||
<JsonForm
|
||||
schema={selectedTemplate.buildVariablesSchema}
|
||||
validator={validator}
|
||||
formData={buildVariables}
|
||||
onChange={handleJsonFormChange}
|
||||
uiSchema={{
|
||||
"ui:submitButtonOptions": {
|
||||
"norender": true,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="enabled"
|
||||
label="状态"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch checkedChildren="启用" unCheckedChildren="禁用"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="sort"
|
||||
label="排序"
|
||||
rules={[{required: true, message: '请输入排序号'}]}
|
||||
>
|
||||
<InputNumber min={0} max={999} style={{width: '100%'}}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeploymentConfigModal;
|
||||
274
frontend/src/pages/Deploy/Deployment/List/index.tsx
Normal file
274
frontend/src/pages/Deploy/Deployment/List/index.tsx
Normal file
@ -0,0 +1,274 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import {PageContainer} from '@ant-design/pro-layout';
|
||||
import {Button, message, Popconfirm, Select} from 'antd';
|
||||
import {PlusOutlined, EditOutlined, DeleteOutlined} from '@ant-design/icons';
|
||||
import {getDeploymentConfigPage, deleteDeploymentConfig} from './service';
|
||||
import {getEnvironmentList} from '../../Environment/List/service';
|
||||
import type {DeploymentConfig, DeploymentConfigQueryParams} from './types';
|
||||
import type {Environment} from '../../Environment/List/types';
|
||||
import DeploymentConfigModal from './components/DeploymentConfigModal';
|
||||
import {ProTable} from '@ant-design/pro-components';
|
||||
import type {ProColumns, ActionType} from '@ant-design/pro-components';
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
const DeploymentConfigList: React.FC = () => {
|
||||
const [environments, setEnvironments] = useState<Environment[]>([]);
|
||||
const [selectedEnvId, setSelectedEnvId] = useState<number>();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [currentConfig, setCurrentConfig] = useState<DeploymentConfig>();
|
||||
const actionRef = React.useRef<ActionType>();
|
||||
|
||||
// 获取环境列表
|
||||
const fetchEnvironments = async () => {
|
||||
try {
|
||||
const data = await getEnvironmentList();
|
||||
setEnvironments(data);
|
||||
if (data.length > 0 && !selectedEnvId) {
|
||||
setSelectedEnvId(data[0].id);
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('获取环境列表失败');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchEnvironments();
|
||||
}, []);
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await deleteDeploymentConfig(id);
|
||||
message.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
if (!selectedEnvId) {
|
||||
message.warning('请先选择环境');
|
||||
return;
|
||||
}
|
||||
setCurrentConfig(undefined);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const handleEdit = (config: DeploymentConfig) => {
|
||||
setCurrentConfig(config);
|
||||
setModalVisible(true);
|
||||
};
|
||||
|
||||
const handleEnvChange = (value: number) => {
|
||||
setSelectedEnvId(value);
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
const renderResourceInfo = (resources?: { cpu?: string; memory?: string }) => {
|
||||
if (!resources?.cpu && !resources?.memory) return '-';
|
||||
const items = [];
|
||||
if (resources.cpu) items.push(`CPU: ${resources.cpu}`);
|
||||
if (resources.memory) items.push(`内存: ${resources.memory}`);
|
||||
return items.join(' / ');
|
||||
};
|
||||
|
||||
const columns: ProColumns<DeploymentConfig>[] = [
|
||||
{
|
||||
title: '应用名称',
|
||||
dataIndex: ['application', 'appName'],
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '应用编码',
|
||||
dataIndex: ['application', 'appCode'],
|
||||
width: 150,
|
||||
copyable: true,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '构建配置',
|
||||
children: [
|
||||
{
|
||||
title: '构建脚本',
|
||||
dataIndex: ['buildConfig', 'buildScript'],
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '构建产物路径',
|
||||
dataIndex: ['buildConfig', 'buildPath'],
|
||||
width: 150,
|
||||
ellipsis: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '部署配置',
|
||||
children: [
|
||||
{
|
||||
title: '副本数',
|
||||
dataIndex: ['deployConfig', 'replicas'],
|
||||
width: 100,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '容器端口',
|
||||
dataIndex: ['deployConfig', 'containerPort'],
|
||||
width: 100,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '资源配置',
|
||||
dataIndex: ['deployConfig', 'resources'],
|
||||
width: 200,
|
||||
ellipsis: true,
|
||||
render: (_, record) => renderResourceInfo(record.deployConfig?.resources),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'enabled',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
valueEnum: {
|
||||
true: {text: '启用', status: 'Success'},
|
||||
false: {text: '禁用', status: 'Default'},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
width: 180,
|
||||
key: 'action',
|
||||
valueType: 'option',
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render: (_, record) => {
|
||||
const buttons = [
|
||||
<Button
|
||||
key="edit"
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
<EditOutlined/> 编辑
|
||||
</Button>,
|
||||
<Popconfirm
|
||||
key="delete"
|
||||
title="确定要删除该部署配置吗?"
|
||||
description="删除后将无法恢复,请谨慎操作"
|
||||
onConfirm={() => handleDelete(record.id)}
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
>
|
||||
<DeleteOutlined/> 删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
];
|
||||
return <div style={{display: 'flex', gap: '8px', justifyContent: 'center'}}>{buttons}</div>;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer
|
||||
header={{
|
||||
title: '部署配置',
|
||||
extra: [
|
||||
<Select
|
||||
key="env-select"
|
||||
value={selectedEnvId}
|
||||
onChange={handleEnvChange}
|
||||
style={{width: 200}}
|
||||
placeholder="请选择环境"
|
||||
>
|
||||
{environments.map((env) => (
|
||||
<Option key={env.id} value={env.id}>
|
||||
{env.envName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>,
|
||||
],
|
||||
}}
|
||||
>
|
||||
<ProTable<DeploymentConfig>
|
||||
columns={columns}
|
||||
actionRef={actionRef}
|
||||
scroll={{x: 'max-content'}}
|
||||
cardBordered
|
||||
request={async (params) => {
|
||||
if (!selectedEnvId) {
|
||||
return {
|
||||
data: [],
|
||||
success: true,
|
||||
total: 0,
|
||||
};
|
||||
}
|
||||
const queryParams: DeploymentConfigQueryParams = {
|
||||
pageSize: params.pageSize,
|
||||
pageNum: params.current,
|
||||
envId: selectedEnvId,
|
||||
enabled: params.enabled as boolean,
|
||||
};
|
||||
const data = await getDeploymentConfigPage(queryParams);
|
||||
return {
|
||||
data: data.content || [],
|
||||
success: true,
|
||||
total: data.totalElements || 0,
|
||||
};
|
||||
}}
|
||||
rowKey="id"
|
||||
search={false}
|
||||
options={{
|
||||
setting: {
|
||||
listsHeight: 400,
|
||||
},
|
||||
}}
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showQuickJumper: true,
|
||||
}}
|
||||
dateFormatter="string"
|
||||
toolBarRender={() => [
|
||||
<Button
|
||||
key="add"
|
||||
type="primary"
|
||||
onClick={handleAdd}
|
||||
icon={<PlusOutlined/>}
|
||||
disabled={!selectedEnvId}
|
||||
>
|
||||
新建部署配置
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
|
||||
{selectedEnvId && (
|
||||
<DeploymentConfigModal
|
||||
visible={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onSuccess={() => {
|
||||
setModalVisible(false);
|
||||
actionRef.current?.reload();
|
||||
}}
|
||||
initialValues={currentConfig}
|
||||
envId={selectedEnvId}
|
||||
/>
|
||||
)}
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeploymentConfigList;
|
||||
34
frontend/src/pages/Deploy/Deployment/List/service.ts
Normal file
34
frontend/src/pages/Deploy/Deployment/List/service.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import request from '@/utils/request';
|
||||
import type {DeploymentConfig, CreateDeploymentConfigRequest, UpdateDeploymentConfigRequest, DeploymentConfigQueryParams, DeployConfigTemplate} from './types';
|
||||
import type {Page} from '@/types/base';
|
||||
|
||||
const BASE_URL = '/api/v1/deployment-configs';
|
||||
const TEMPLATE_URL = '/api/v1/deploy-app-config';
|
||||
|
||||
// 获取部署配置分页列表
|
||||
export const getDeploymentConfigPage = (params: DeploymentConfigQueryParams) =>
|
||||
request.get<Page<DeploymentConfig>>(`${BASE_URL}/page`, {params});
|
||||
|
||||
// 创建部署配置
|
||||
export const createDeploymentConfig = (data: CreateDeploymentConfigRequest) =>
|
||||
request.post<DeploymentConfig>(BASE_URL, data);
|
||||
|
||||
// 更新部署配置
|
||||
export const updateDeploymentConfig = (data: UpdateDeploymentConfigRequest) =>
|
||||
request.put<DeploymentConfig>(`${BASE_URL}/${data.id}`, data);
|
||||
|
||||
// 删除部署配置
|
||||
export const deleteDeploymentConfig = (id: number) =>
|
||||
request.delete(`${BASE_URL}/${id}`);
|
||||
|
||||
// 获取部署配置详情
|
||||
export const getDeploymentConfig = (id: number) =>
|
||||
request.get<DeploymentConfig>(`${BASE_URL}/${id}`);
|
||||
|
||||
// 获取环境下的所有部署配置
|
||||
export const getDeploymentConfigsByEnv = (envId: number) =>
|
||||
request.get<DeploymentConfig[]>(`${BASE_URL}/env/${envId}`);
|
||||
|
||||
// 获取部署配置模板列表
|
||||
export const getDeployConfigTemplates = () =>
|
||||
request.get<DeployConfigTemplate[]>(`${TEMPLATE_URL}/defined`);
|
||||
51
frontend/src/pages/Deploy/Deployment/List/types.ts
Normal file
51
frontend/src/pages/Deploy/Deployment/List/types.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import {BaseResponse, BaseRequest, BaseQuery} from '@/types/base';
|
||||
import {Environment} from '../../Environment/List/types';
|
||||
import {Application} from '../../Application/List/types';
|
||||
import {DevelopmentLanguageTypeEnum} from '../../Application/List/types';
|
||||
import {BuildTypeEnum} from '../../Environment/List/types';
|
||||
import type {JsonNode} from '@/types/common';
|
||||
|
||||
// 部署配置模板
|
||||
export interface DeployConfigTemplate {
|
||||
code: string;
|
||||
name: string;
|
||||
buildType: BuildTypeEnum;
|
||||
languageType: DevelopmentLanguageTypeEnum;
|
||||
buildVariablesSchema: JsonNode;
|
||||
}
|
||||
|
||||
// 部署配置基础信息
|
||||
export interface DeploymentConfig extends BaseResponse {
|
||||
tenantCode: string;
|
||||
envId: number;
|
||||
environment: Environment;
|
||||
appId: number;
|
||||
application: Application;
|
||||
templateCode: string;
|
||||
buildVariables: Record<string, any>;
|
||||
enabled: boolean;
|
||||
sort: number;
|
||||
}
|
||||
|
||||
// 创建部署配置请求参数
|
||||
export interface CreateDeploymentConfigRequest extends BaseRequest {
|
||||
tenantCode: string;
|
||||
envId: number;
|
||||
appId: number;
|
||||
templateCode: string;
|
||||
buildVariables: Record<string, any>;
|
||||
enabled: boolean;
|
||||
sort: number;
|
||||
}
|
||||
|
||||
// 更新部署配置请求参数
|
||||
export interface UpdateDeploymentConfigRequest extends CreateDeploymentConfigRequest {
|
||||
id: number;
|
||||
}
|
||||
|
||||
// 分页查询参数
|
||||
export interface DeploymentConfigQueryParams extends BaseQuery {
|
||||
envId?: number;
|
||||
appId?: number;
|
||||
enabled?: boolean;
|
||||
}
|
||||
@ -41,6 +41,7 @@ const NodeDesignForm = lazy(() => import('../pages/Workflow/NodeDesign/Design'))
|
||||
const ProjectGroupList = lazy(() => import('../pages/Deploy/ProjectGroup/List'));
|
||||
const ApplicationList = lazy(() => import('../pages/Deploy/Application/List'));
|
||||
const EnvironmentList = lazy(() => import('../pages/Deploy/Environment/List'));
|
||||
const DeploymentConfigList = lazy(() => import('../pages/Deploy/Deployment/List'));
|
||||
const External = lazy(() => import('../pages/Deploy/External'));
|
||||
|
||||
// 创建路由
|
||||
@ -84,6 +85,10 @@ const router = createBrowserRouter([
|
||||
path: 'environments',
|
||||
element: <Suspense fallback={<LoadingComponent/>}><EnvironmentList/></Suspense>
|
||||
},
|
||||
{
|
||||
path: 'deployment',
|
||||
element: <Suspense fallback={<LoadingComponent/>}><DeploymentConfigList/></Suspense>
|
||||
},
|
||||
{
|
||||
path: 'external',
|
||||
element: (
|
||||
|
||||
19
frontend/src/types/common.ts
Normal file
19
frontend/src/types/common.ts
Normal file
@ -0,0 +1,19 @@
|
||||
// JSON Schema 相关类型
|
||||
export type JsonNode = {
|
||||
type?: string;
|
||||
properties?: Record<string, JsonNode>;
|
||||
items?: JsonNode;
|
||||
required?: string[];
|
||||
title?: string;
|
||||
description?: string;
|
||||
enum?: any[];
|
||||
enumNames?: string[];
|
||||
default?: any;
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
pattern?: string;
|
||||
format?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user