diff --git a/chaosmeta-platform-frontend/package-lock.json b/chaosmeta-platform-frontend/package-lock.json index 78b4a55..7bb5ae5 100644 --- a/chaosmeta-platform-frontend/package-lock.json +++ b/chaosmeta-platform-frontend/package-lock.json @@ -13,13 +13,13 @@ "@types/react-syntax-highlighter": "^15.5.7", "@umijs/max": "^4.0.72", "antd": "^5.4.0", - "cron-parser": "^4.9.0", "cronstrue": "^2.31.0", "crypto-js": "^4.1.1", + "dayjs": "^1.11.10", "echarts": "^5.4.3", - "html-to-image": "^1.11.11", - "moment": "^2.29.4", + "highlight.js": "^11.9.0", "react-cookies": "^0.1.1", + "react-highlight": "^0.15.0", "react-markdown": "^8.0.7", "react-syntax-highlighter": "^15.5.0", "uuid": "^9.0.0" @@ -29,6 +29,7 @@ "@types/react": "^18.0.33", "@types/react-cookies": "^0.1.0", "@types/react-dom": "^18.0.11", + "@types/react-highlight": "^0.12.7", "@types/uuid": "^9.0.2", "lint-staged": "^13.2.0", "prettier": "^2.8.7", @@ -4256,6 +4257,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-highlight": { + "version": "0.12.7", + "resolved": "https://registry.npmmirror.com/@types/react-highlight/-/react-highlight-0.12.7.tgz", + "integrity": "sha512-szwwyD8LpTtsk4/NjHwyTofYDoazo3rmGOMkgDYy/V3zD4VNh6yS8tk70Qvj5czdBCnOV/AtaLf4VxBfaelwEQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-syntax-highlighter": { "version": "15.5.7", "resolved": "https://registry.npmmirror.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.7.tgz", @@ -7266,17 +7276,6 @@ "sha.js": "^2.4.8" } }, - "node_modules/cron-parser": { - "version": "4.9.0", - "resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-4.9.0.tgz", - "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", - "dependencies": { - "luxon": "^3.2.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/cronstrue": { "version": "2.31.0", "resolved": "https://registry.npmmirror.com/cronstrue/-/cronstrue-2.31.0.tgz", @@ -7570,10 +7569,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", - "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==", - "license": "MIT" + "version": "1.11.10", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -9805,11 +9803,11 @@ } }, "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "version": "11.9.0", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.9.0.tgz", + "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==", "engines": { - "node": "*" + "node": ">=12.0.0" } }, "node_modules/history": { @@ -9929,11 +9927,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/html-to-image": { - "version": "1.11.11", - "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz", - "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==" - }, "node_modules/html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -11566,6 +11559,14 @@ "highlight.js": "~10.7.0" } }, + "node_modules/lowlight/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -11582,6 +11583,8 @@ "version": "3.4.1", "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.4.1.tgz", "integrity": "sha512-2USspxOCXWGIKHwuQ9XElxPPYrDOJHDQ5DQ870CoD+CxJbBnRDIBCfhioUJJjct7BKOy80Ia8cVstIcIMb/0+Q==", + "optional": true, + "peer": true, "engines": { "node": ">=12" } @@ -14633,6 +14636,22 @@ "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-highlight": { + "version": "0.15.0", + "resolved": "https://registry.npmmirror.com/react-highlight/-/react-highlight-0.15.0.tgz", + "integrity": "sha512-5uV/b/N4Z421GSVVe05fz+OfTsJtFzx/fJBdafZyw4LS70XjIZwgEx3Lrkfc01W/RzZ2Dtfb0DApoaJFAIKBtA==", + "dependencies": { + "highlight.js": "^10.5.0" + } + }, + "node_modules/react-highlight/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/react-intl": { "version": "3.12.1", "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-3.12.1.tgz", @@ -14804,6 +14823,14 @@ "react": ">= 0.14.0" } }, + "node_modules/react-syntax-highlighter/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/reactcss": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", @@ -20214,6 +20241,15 @@ "@types/react": "*" } }, + "@types/react-highlight": { + "version": "0.12.7", + "resolved": "https://registry.npmmirror.com/@types/react-highlight/-/react-highlight-0.12.7.tgz", + "integrity": "sha512-szwwyD8LpTtsk4/NjHwyTofYDoazo3rmGOMkgDYy/V3zD4VNh6yS8tk70Qvj5czdBCnOV/AtaLf4VxBfaelwEQ==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-syntax-highlighter": { "version": "15.5.7", "resolved": "https://registry.npmmirror.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.7.tgz", @@ -22348,14 +22384,6 @@ "sha.js": "^2.4.8" } }, - "cron-parser": { - "version": "4.9.0", - "resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-4.9.0.tgz", - "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", - "requires": { - "luxon": "^3.2.1" - } - }, "cronstrue": { "version": "2.31.0", "resolved": "https://registry.npmmirror.com/cronstrue/-/cronstrue-2.31.0.tgz", @@ -22538,9 +22566,9 @@ } }, "dayjs": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", - "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==" + "version": "1.11.10", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" }, "debug": { "version": "4.3.4", @@ -24036,9 +24064,9 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, "highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + "version": "11.9.0", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.9.0.tgz", + "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==" }, "history": { "version": "5.3.0", @@ -24121,11 +24149,6 @@ "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==" }, - "html-to-image": { - "version": "1.11.11", - "resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.11.tgz", - "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==" - }, "html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", @@ -25165,6 +25188,13 @@ "requires": { "fault": "^1.0.0", "highlight.js": "~10.7.0" + }, + "dependencies": { + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + } } }, "lru-cache": { @@ -25178,7 +25208,9 @@ "luxon": { "version": "3.4.1", "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.4.1.tgz", - "integrity": "sha512-2USspxOCXWGIKHwuQ9XElxPPYrDOJHDQ5DQ870CoD+CxJbBnRDIBCfhioUJJjct7BKOy80Ia8cVstIcIMb/0+Q==" + "integrity": "sha512-2USspxOCXWGIKHwuQ9XElxPPYrDOJHDQ5DQ870CoD+CxJbBnRDIBCfhioUJJjct7BKOy80Ia8cVstIcIMb/0+Q==", + "optional": true, + "peer": true }, "make-dir": { "version": "2.1.0", @@ -27203,6 +27235,21 @@ "shallowequal": "^1.1.0" } }, + "react-highlight": { + "version": "0.15.0", + "resolved": "https://registry.npmmirror.com/react-highlight/-/react-highlight-0.15.0.tgz", + "integrity": "sha512-5uV/b/N4Z421GSVVe05fz+OfTsJtFzx/fJBdafZyw4LS70XjIZwgEx3Lrkfc01W/RzZ2Dtfb0DApoaJFAIKBtA==", + "requires": { + "highlight.js": "^10.5.0" + }, + "dependencies": { + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + } + } + }, "react-intl": { "version": "3.12.1", "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-3.12.1.tgz", @@ -27318,6 +27365,13 @@ "lowlight": "^1.17.0", "prismjs": "^1.27.0", "refractor": "^3.6.0" + }, + "dependencies": { + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + } } }, "reactcss": { diff --git a/chaosmeta-platform-frontend/package.json b/chaosmeta-platform-frontend/package.json index 6a5234a..c75dda1 100644 --- a/chaosmeta-platform-frontend/package.json +++ b/chaosmeta-platform-frontend/package.json @@ -14,16 +14,14 @@ "@ant-design/pro-components": "^2.4.4", "@dnd-kit/core": "^6.0.8", "@dnd-kit/sortable": "^7.0.2", - "@types/react-syntax-highlighter": "^15.5.7", "@umijs/max": "^4.0.72", "antd": "^5.4.0", "cronstrue": "^2.31.0", "crypto-js": "^4.1.1", "dayjs": "^1.11.10", "echarts": "^5.4.3", + "react-ace": "^10.1.0", "react-cookies": "^0.1.1", - "react-markdown": "^8.0.7", - "react-syntax-highlighter": "^15.5.0", "uuid": "^9.0.0" }, "devDependencies": { diff --git a/chaosmeta-platform-frontend/src/components/DynamicForm/UnitInput.tsx b/chaosmeta-platform-frontend/src/components/DynamicForm/UnitInput.tsx index 9cda424..cd890e0 100644 --- a/chaosmeta-platform-frontend/src/components/DynamicForm/UnitInput.tsx +++ b/chaosmeta-platform-frontend/src/components/DynamicForm/UnitInput.tsx @@ -18,10 +18,11 @@ interface IProps { parentName?: string; onChange?: any; value?: string; + handleEditNode?: any; } const UnitInpt = (props: IProps) => { - const { field, form, parentName, onChange, value } = props; + const { field, form, parentName, onChange, value, handleEditNode } = props; const [options, setOptions] = useState([]); const [curUnitValue, setCurUnitValue] = useState(''); @@ -34,11 +35,8 @@ const UnitInpt = (props: IProps) => { isBlur?: boolean; }) => { const unitValue = value || curUnitValue; - // 如果存在值, key为持续时长时不做处理 - if ( - form.getFieldValue(formatFormName(field, parentName)) && - field.key !== 'duration' - ) { + // 如果存在值 + if (form.getFieldValue(formatFormName(field, parentName))) { const inputValue = form.getFieldValue(formatFormName(field, parentName)); let newValue = inputValue; // 找出原始单位 @@ -56,6 +54,9 @@ const UnitInpt = (props: IProps) => { // 没有单位直接将填充值与新单位拼接 newValue = inputValue + unitValue; } + if (field?.key === 'duration') { + handleEditNode('duration', newValue); + } form.setFieldValue(formatFormName(field, parentName), newValue); } }; diff --git a/chaosmeta-platform-frontend/src/components/DynamicForm/index.tsx b/chaosmeta-platform-frontend/src/components/DynamicForm/index.tsx index 9ded528..2348df8 100644 --- a/chaosmeta-platform-frontend/src/components/DynamicForm/index.tsx +++ b/chaosmeta-platform-frontend/src/components/DynamicForm/index.tsx @@ -1,9 +1,9 @@ import { formatFormName, getIntlText } from '@/utils/format'; import { useIntl } from '@umijs/max'; import { Form, Input, Radio, Select } from 'antd'; +import KubernetesNamespaceSelect from '../Select/KubernetesNamespaceSelect'; import ShowText from '../ShowText'; import UnitInput from './UnitInput'; -import { DividerLine } from './style'; interface Field { id: number; @@ -27,6 +27,8 @@ interface Props { parentName?: string; readonly?: boolean; form?: any; + // 用于更新节点信息 + handleEditNode?: any; } /** @@ -35,7 +37,7 @@ interface Props { * @returns */ const DynamicForm = (props: Props) => { - const { fieldList, parentName, readonly, form } = props; + const { fieldList, parentName, readonly, form, handleEditNode } = props; const intl = useIntl(); /** @@ -44,7 +46,7 @@ const DynamicForm = (props: Props) => { * @returns */ const renderItem = (field: any) => { - const { valueRule, valueType } = field; + const { valueRule, valueType, key } = field; // 无论valueType类型是什么,后端统一接收string // if (valueType === 'int') { // return ( @@ -54,6 +56,10 @@ const DynamicForm = (props: Props) => { // /> // ); // } + // key 为namespace时需要特殊渲染 + if (key === 'namespace') { + return ; + } if (valueType === 'bool') { return ( @@ -62,6 +68,7 @@ const DynamicForm = (props: Props) => { ); } + // valueType为stringlist渲染为tags类型的select组件 if (valueType === 'stringlist') { let options: string[] = []; if (valueRule) { @@ -79,6 +86,7 @@ const DynamicForm = (props: Props) => { ); } + // 如果valueRule不包含-、>、<、=,则渲染Select组件 if ( valueRule && !valueRule.includes('-') && @@ -91,7 +99,10 @@ const DynamicForm = (props: Props) => { options = valueRule?.split(','); } return ( - {options.map((option) => { return ( @@ -106,11 +117,25 @@ const DynamicForm = (props: Props) => { return ; }; - const initValue = (defaultValue: any) => { + const initValue = (item: any) => { + const { defaultValue, valueRule } = item; + let value = undefined; + // 默认值不存在且为select时,默认值为第一个选项 + if ( + valueRule && + !valueRule.includes('-') && + !valueRule.includes('>') && + !valueRule.includes('<') && + !valueRule.includes('=') && + !defaultValue + ) { + const options = valueRule?.split(','); + value = options[0]; + } if (defaultValue || defaultValue === 0) { - return defaultValue; + value = defaultValue; } - return undefined; + return value; }; /** @@ -172,23 +197,18 @@ const DynamicForm = (props: Props) => { return ( <> - {fieldList?.map((item: Field, index: number) => { + {fieldList?.map((item: Field) => { const { id, required, defaultValue } = item; if (readonly) { return ( <> - {index === 0 && item?.execType?.includes('common') && ( -
- {intl.formatMessage({ id: 'generalParameters' })} -
- )} - {!item?.execType?.includes('common') && - fieldList[index - 1]?.execType?.includes('common') && ( - - )} @@ -199,19 +219,14 @@ const DynamicForm = (props: Props) => { } return ( <> - {index === 0 && item?.execType?.includes('common') && ( -
- {intl.formatMessage({ id: 'generalParameters' })} -
- )} - {!item?.execType?.includes('common') && - fieldList[index - 1]?.execType?.includes('common') && ( - - )} { }, }, ]} - initialValue={initValue(defaultValue)} + initialValue={initValue(item)} > {/* 带有单位的特殊处理 */} {item.unit ? ( - + ) : ( renderItem(item) )} diff --git a/chaosmeta-platform-frontend/src/components/Select/KubernetesDeploymentNameSelect/index.tsx b/chaosmeta-platform-frontend/src/components/Select/KubernetesDeploymentNameSelect/index.tsx new file mode 100644 index 0000000..6778b47 --- /dev/null +++ b/chaosmeta-platform-frontend/src/components/Select/KubernetesDeploymentNameSelect/index.tsx @@ -0,0 +1,97 @@ +import { queryDeploymentNameList } from '@/services/chaosmeta/KubernetesController'; +import { useIntl, useRequest } from '@umijs/max'; +import { Empty, Select, Spin, message } from 'antd'; +import { useEffect, useState } from 'react'; + +interface IProps { + value?: string; + onChange?: (value?: string) => void; + /**回显时可传入list */ + list?: any[]; + mode?: 'multiple' | 'tags'; + placeholder?: string; + style?: any; + form?: any; + kubernetesNamespace?: any; +} + +const KubernetesPodSelect = (props: IProps) => { + const intl = useIntl(); + const { + value, + onChange, + list, + mode, + placeholder = intl.formatMessage({ id: 'selectPlaceholder' }), + style, + form, + kubernetesNamespace, + } = props; + const [namespaceList, setNamespaceList] = useState([]); + const { Option } = Select; + + useEffect(() => { + if (list && list?.length > 0) { + setNamespaceList(list); + } + }, [list]); + + const getPodList = useRequest(queryDeploymentNameList, { + manual: true, + formatResult: (res: any) => res, + debounceInterval: 300, + onSuccess: (res) => { + if (res?.success) { + setNamespaceList(res?.data?.list || []); + } else { + message.error(res?.message); + } + }, + }); + + useEffect(() => { + // 此项的展示是要依赖namespace的值进行检索,所以当namespace值改变时需要清空列表项和值 + setNamespaceList([]); + }, [kubernetesNamespace]); + + return ( + + ); +}; + +export default KubernetesPodSelect; diff --git a/chaosmeta-platform-frontend/src/components/Select/KubernetesPodNodeSelect/index.tsx b/chaosmeta-platform-frontend/src/components/Select/KubernetesPodNodeSelect/index.tsx index ccb36f4..cbe6124 100644 --- a/chaosmeta-platform-frontend/src/components/Select/KubernetesPodNodeSelect/index.tsx +++ b/chaosmeta-platform-frontend/src/components/Select/KubernetesPodNodeSelect/index.tsx @@ -1,4 +1,4 @@ -import { queryPodNodeList } from '@/services/chaosmeta/KubernetesController'; +import { queryNodeNameList } from '@/services/chaosmeta/KubernetesController'; import { useIntl, useRequest } from '@umijs/max'; import { Empty, Select, Spin, message } from 'antd'; import { useEffect, useState } from 'react'; @@ -32,7 +32,7 @@ const KubernetesPodSelect = (props: IProps) => { } }, [list]); - const getPodList = useRequest(queryPodNodeList, { + const getPodList = useRequest(queryNodeNameList, { manual: true, formatResult: (res: any) => res, debounceInterval: 300, diff --git a/chaosmeta-platform-frontend/src/components/Select/KubernetesPodSelect/index.tsx b/chaosmeta-platform-frontend/src/components/Select/KubernetesPodSelect/index.tsx index de1f452..8fc0ba7 100644 --- a/chaosmeta-platform-frontend/src/components/Select/KubernetesPodSelect/index.tsx +++ b/chaosmeta-platform-frontend/src/components/Select/KubernetesPodSelect/index.tsx @@ -1,4 +1,4 @@ -import { queryPodLIst } from '@/services/chaosmeta/KubernetesController'; +import { queryPodNameList } from '@/services/chaosmeta/KubernetesController'; import { useIntl, useRequest } from '@umijs/max'; import { Empty, Select, Spin, message } from 'antd'; import { useEffect, useState } from 'react'; @@ -36,7 +36,7 @@ const KubernetesPodSelect = (props: IProps) => { } }, [list]); - const getPodList = useRequest(queryPodLIst, { + const getPodList = useRequest(queryPodNameList, { manual: true, formatResult: (res: any) => res, debounceInterval: 300, @@ -52,7 +52,6 @@ const KubernetesPodSelect = (props: IProps) => { useEffect(() => { // 此项的展示是要依赖namespace的值进行检索,所以当namespace值改变时需要清空列表项和值 setNamespaceList([]); - form.setFieldValue(['exec_range', 'target_name'], undefined); }, [kubernetesNamespace]); return ( diff --git a/chaosmeta-platform-frontend/src/locales/en-US.ts b/chaosmeta-platform-frontend/src/locales/en-US.ts index 4339433..eff71a8 100644 --- a/chaosmeta-platform-frontend/src/locales/en-US.ts +++ b/chaosmeta-platform-frontend/src/locales/en-US.ts @@ -161,7 +161,11 @@ const publicText = { all: 'all', selectAll: 'select all', submit: 'submit', - generalParameters: 'General parameters' + commonParameters: 'Common parameters', + atomicCapabilities: 'atomic capabilities', + faultParameters: 'Fault parameters', + executionParameters: 'Execution parameters', + judgmentParameters: 'Judgment parameters', }; // 实验 @@ -292,7 +296,7 @@ const login = { 'The username can be in Chinese and English, and the length should not exceed 64 characters.', notAccount: 'Don’t have an account yet?', haveAccount: 'Already have an account?', - 'reister.success': 'Registration successful, please log in' + 'reister.success': 'Registration successful, please log in', }; // 账号相关 const account = { @@ -303,29 +307,38 @@ const account = { admin: 'admin', generalUser: 'general user', adminDescription: 'Have all permissions', - generalUserDescription: 'You can log in to view, and superimpose the permissions in the space.', - 'account.delete.title': 'Are you sure you want to delete the currently selected account?', - 'account.delete.content': 'Users who delete their accounts will not be able to log in to the platform, and they can only re-register if they want to use it again!', - 'account.delete.success': 'You have successfully deleted the selected members', - 'account.role.update': 'User role modified successfully' -} + generalUserDescription: + 'You can log in to view, and superimpose the permissions in the space.', + 'account.delete.title': + 'Are you sure you want to delete the currently selected account?', + 'account.delete.content': + 'Users who delete their accounts will not be able to log in to the platform, and they can only re-register if they want to use it again!', + 'account.delete.success': + 'You have successfully deleted the selected members', + 'account.role.update': 'User role modified successfully', +}; // 空间管理 const spaceManagement = { 'spaceManagement.title': 'Space management', 'spaceManagement.spaceName.placeholder': 'Please enter the space name', 'spaceManagement.spaceMember.placeholder': 'Please enter space members', - 'spaceManagement.alert': 'Members with read and write permissions in the contact space can be added as space members', + 'spaceManagement.alert': + 'Members with read and write permissions in the contact space can be added as space members', 'spaceManagement.member': 'space member', 'spaceManagement.tab.all': 'All spaces', 'spaceManagement.tab.related': 'My related', - 'spaceManagement.delete.title': 'Are you sure you want to delete the currently selected space?', - 'spaceManagement.delete.success': 'You have successfully deleted the selected space', - 'spaceManagement.noAuth.tip': 'You do not have permission to this space, please contact a read-write member', - 'spaceManagement.noAuth.readonly.tip': 'Read-only users are currently unable to use this feature', + 'spaceManagement.delete.title': + 'Are you sure you want to delete the currently selected space?', + 'spaceManagement.delete.success': + 'You have successfully deleted the selected space', + 'spaceManagement.noAuth.tip': + 'You do not have permission to this space, please contact a read-write member', + 'spaceManagement.noAuth.readonly.tip': + 'Read-only users are currently unable to use this feature', 'spaceManagement.write': 'read and write members', 'spaceManagement.experimentCount': 'number of experiments', -} +}; export default { ...routeMenu, @@ -344,5 +357,5 @@ export default { ...tagManageMent, ...login, ...account, - ...spaceManagement + ...spaceManagement, }; diff --git a/chaosmeta-platform-frontend/src/locales/zh-CN.ts b/chaosmeta-platform-frontend/src/locales/zh-CN.ts index b818ffc..b617fff 100644 --- a/chaosmeta-platform-frontend/src/locales/zh-CN.ts +++ b/chaosmeta-platform-frontend/src/locales/zh-CN.ts @@ -158,7 +158,11 @@ const publicText = { all: '全部', selectAll: '全选', submit: '提交', - generalParameters: '通用参数' + commonParameters: '通用参数', + atomicCapabilities: '原子能力', + faultParameters: '故障参数', + executionParameters:'执行参数', + judgmentParameters: '判定参数' }; // 实验 diff --git a/chaosmeta-platform-frontend/src/pages/GlobalSetting/Space/SpaceList.tsx b/chaosmeta-platform-frontend/src/pages/GlobalSetting/Space/SpaceList.tsx index 60bc38f..24a079b 100644 --- a/chaosmeta-platform-frontend/src/pages/GlobalSetting/Space/SpaceList.tsx +++ b/chaosmeta-platform-frontend/src/pages/GlobalSetting/Space/SpaceList.tsx @@ -17,7 +17,20 @@ const SpaceList: React.FC = (props) => { const items = (spaceId: number) => [ { - label:
{intl.formatMessage({ id: 'spaceSetting' })}
, + label: ( +
{ + history.push({ + pathname: '/space/setting', + query: { + spaceId: spaceId?.toString(), + }, + }); + }} + > + {intl.formatMessage({ id: 'spaceSetting' })} +
+ ), key: 'spaceSetting', }, { diff --git a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/ArrangeContent.tsx b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/ArrangeContent.tsx index 414de3d..ba85364 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/ArrangeContent.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/ArrangeContent.tsx @@ -384,6 +384,7 @@ const ArrangeContent: React.FC = (props) => { dragtype: 'item', exec_id: activeItem?.id, name: getIntlName(activeItem), + exec_name: getIntlName(activeItem), // 当前节点信息是否进行配置完成, wait类型只需要配置时长,所以这里默认为true nodeInfoState: activeItem?.exec_type === 'wait', }); @@ -401,6 +402,7 @@ const ArrangeContent: React.FC = (props) => { // 将节点库id保存 exec_id: activeItem?.id, name: getIntlName(activeItem), + exec_name: getIntlName(activeItem), // 当前节点信息是否进行配置完成 nodeInfoState: activeItem?.exec_type === 'wait', }); diff --git a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/DroppableItem.tsx b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/DroppableItem.tsx index 5895a7c..9b2efb1 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/DroppableItem.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/DroppableItem.tsx @@ -1,5 +1,5 @@ import { arrangeNodeTypeColors, scaleStepMap } from '@/constants'; -import { formatDuration, getIntlName } from '@/utils/format'; +import { formatDuration } from '@/utils/format'; import { InfoCircleOutlined } from '@ant-design/icons'; import { useSortable } from '@dnd-kit/sortable'; import { useIntl } from '@umijs/max'; @@ -161,7 +161,7 @@ const DroppableItem: React.FC = (props) => { style={{ color: '#FF4D4F', marginRight: '4px' }} /> )} - {getIntlName(item)} + {item?.name}
{curDuration}s
diff --git a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/DynamicFormRender.tsx b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/DynamicFormRender.tsx new file mode 100644 index 0000000..9b81ab2 --- /dev/null +++ b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/DynamicFormRender.tsx @@ -0,0 +1,161 @@ +import DynamicForm from '@/components/DynamicForm'; +import { useIntl } from '@umijs/max'; +import { useEffect, useState } from 'react'; + +interface IProps { + fieldList: any[]; + form?: any; + nodeType: string; + readonly?: boolean; + handleEditNode?: any; +} +/** + * 动态表单模块区分渲染 + * @returns + */ +const DynamicFormRender: React.FC = (props) => { + const { fieldList, form, nodeType, readonly, handleEditNode } = props; + const intl = useIntl(); + // 度量通用参数表单 + const [measureCommonFields, setMeasureCommonFields] = useState([]); + // 度量判定参数表单 + const [measureJudgeFields, setMeasureJudgeFields] = useState([]); + // 度量执行参数表单 + const [measureExecuteFields, setMeasureExecuteFields] = useState([]); + // 故障参数表单 + const [faultFields, setFaultFields] = useState([]); + // 流量通用参数表单 + const [flowCommonFields, setFlowCommonFields] = useState([]); + // 流量执行参数表单 + const [flowExecuteFields, setflowExecuteFields] = useState([]); + + // 流量通用参数keys + const flowcommonParamsKeys = [ + // 'flowType', + 'duration', + 'parallelism', + 'source', + ]; + + // 度量通用参数keys + const measureCommonParamsKeys = [ + // 'measureType', + 'duration', + 'interval', + 'successCount', + 'failedCount', + ]; + // 度量判定参数keys + const measureJudgeParamsKeys = ['judgeType', 'judgeValue']; + + useEffect(() => { + if (fieldList?.length > 0) { + if (nodeType === 'fault') { + setFaultFields(fieldList); + } + if (nodeType === 'measure') { + // 度量通用参数 + setMeasureCommonFields(() => { + return fieldList?.filter((item) => + measureCommonParamsKeys?.includes(item?.key), + ); + }); + // 度量判定参数 + setMeasureJudgeFields(() => { + return fieldList?.filter((item) => + measureJudgeParamsKeys?.includes(item?.key), + ); + }); + // 度量执行参数 - 不属于通用和判定的即为执行参数 + setMeasureExecuteFields(() => { + return fieldList?.filter( + (item) => + !measureJudgeParamsKeys?.includes(item?.key) && + !measureCommonParamsKeys?.includes(item?.key) && + item?.key !== 'measureType', + ); + }); + } + if (nodeType === 'flow') { + // 流量通用参数 + setFlowCommonFields(() => { + return fieldList?.filter((item) => + flowcommonParamsKeys?.includes(item?.key), + ); + }); + // 流量执行参数 - 不属于通用的即为执行参数 + setflowExecuteFields(() => { + return fieldList?.filter( + (item) => + !flowcommonParamsKeys?.includes(item?.key) && + item?.key !== 'flowType', + ); + }); + } + } + }, [fieldList, nodeType]); + + const fieldsFormRender = (fields: any[], title: string) => { + return ( + fields?.length > 0 && ( + <> + {readonly ? ( +
+ {intl.formatMessage({ id: title })} +
+ ) : ( +
{intl.formatMessage({ id: title })}
+ )} + + + + ) + ); + }; + + return ( + <> + {/* 故障参数 */} + {nodeType === 'fault' && ( + <>{fieldsFormRender(faultFields, 'faultParameters')} + )} + {nodeType === 'measure' && ( + <> + item?.key === 'measureType')} + parentName={'args_value'} + form={form} + readonly={readonly} + /> + {/* 度量通用参数表单 */} + {fieldsFormRender(measureCommonFields, 'commonParameters')} + {/* 度量判定参数表单 */} + {fieldsFormRender(measureJudgeFields, 'judgmentParameters')} + {/* 度量执行参数表单 */} + {fieldsFormRender(measureExecuteFields, 'executionParameters')} + + )} + {nodeType === 'flow' && ( + <> + item?.key === 'flowType')} + parentName={'args_value'} + form={form} + readonly={readonly} + /> + {/* 流量通用参数表单 */} + {fieldsFormRender(flowCommonFields, 'commonParameters')} + {/* 流量执行参数表单 */} + {fieldsFormRender(flowExecuteFields, 'executionParameters')} + + )} + + ); +}; +export default DynamicFormRender; diff --git a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/NodeConfig.tsx b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/NodeConfig.tsx index db3b46c..0fbc800 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/NodeConfig.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/components/NodeConfig.tsx @@ -1,4 +1,4 @@ -import DynamicForm from '@/components/DynamicForm'; +import KubernetesDeploymentNameSelect from '@/components/Select/KubernetesDeploymentNameSelect'; import KubernetesNamespaceSelect from '@/components/Select/KubernetesNamespaceSelect'; import KubernetesPodNodeSelect from '@/components/Select/KubernetesPodNodeSelect'; import KubernetesPodSelect from '@/components/Select/KubernetesPodSelect'; @@ -9,6 +9,7 @@ import { queryFlowNodeFields, queryMeasureNodeFields, } from '@/services/chaosmeta/ExperimentController'; +import { queryFaultNodeDetail } from '@/services/chaosmeta/KubernetesController'; import { formatDuration } from '@/utils/format'; import { CheckOutlined, @@ -21,7 +22,6 @@ import { Button, Form, Input, - InputNumber, Popconfirm, Select, Space, @@ -30,6 +30,7 @@ import { } from 'antd'; import React, { useEffect, useState } from 'react'; import { NodeConfigContainer } from '../style'; +import DynamicFormRender from './DynamicFormRender'; interface IProps { form: any; @@ -57,6 +58,8 @@ const NodeConfig: React.FC = (props) => { const [fieldList, setFieldList] = useState([]); const [durationType, setDurationType] = useState('second'); const [kubernetesNamespace, setKubernetesNamespace] = useState(''); + // 用于判断当前节点位于哪个父节点下 + const [targetName, setTargetName] = useState(''); const intl = useIntl(); /** @@ -70,13 +73,18 @@ const NodeConfig: React.FC = (props) => { const parentIndex = values?.findIndex( (item: { row: any }) => item?.row === activeCol?.parentId, ); - if (parentIndex !== -1 && activeCol?.index >= 0) { + if ( + parentIndex !== -1 && + activeCol?.index >= 0 && + values[parentIndex]?.children?.length > 0 + ) { values[parentIndex].children[activeCol.index][key] = value; // 同时更新保存当前节点信息 setActiveCol((origin: any) => { return { ...origin, [key]: value, state: true }; }); } + return values; // 返回更新后的 values 数组 }); }; @@ -103,11 +111,11 @@ const NodeConfig: React.FC = (props) => { */ const hanldeUpdateNode = (params: any) => { let curTime = form.getFieldValue('duration'); - if (durationType === 'minute') { - curTime = `${curTime}m`; - } else { - curTime = `${curTime}s`; - } + // if (durationType === 'minute') { + // curTime = `${curTime}m`; + // } else { + // curTime = `${curTime}s`; + // } setArrangeList((result: any) => { const values = JSON.parse(JSON.stringify(result)); // 将 result 对象深拷贝一份 @@ -160,7 +168,7 @@ const NodeConfig: React.FC = (props) => { setKubernetesNamespace(activeCol?.exec_range?.target_namespace); form.setFieldsValue({ ...activeCol, - duration: initSecond, + duration: `${initSecond}s`, exec_type_name: (getLocale() === 'en-US' ? nodeTypeMapUS : nodeTypeMap)[ activeCol?.exec_type ], @@ -213,6 +221,19 @@ const NodeConfig: React.FC = (props) => { }, }); + /** + * 根据targetid获取该节点信息,用于判断该节点是否位于node或pod下 + */ + const getFaultNodeDetail = useRequest(queryFaultNodeDetail, { + manual: true, + formatResult: (res) => res, + onSuccess: (res: any) => { + if (res?.code === 200) { + setTargetName(res?.data?.name); + } + }, + }); + /** * @description: 处理删除节点的方法 */ @@ -242,20 +263,18 @@ const NodeConfig: React.FC = (props) => { // 是否为node下的节点 const isNode = () => { - // 父节点有两个node,一种是scope_id为2,另一种是scope_id为3下的target_id为23(Kubernetes下的node) - return ( - activeCol?.scope_id === 2 || - (activeCol?.scope_id === 3 && activeCol?.target_id === 23) - ); + // 只有故障节点下的才有可能有node节点 + if (activeCol?.exec_type !== 'fault') { + return false; + } + // 父节点有两个node,一种是scope_id为2,另一种是通过接口查询name为node + return activeCol?.scope_id === 2 || targetName === 'node'; }; // 是否为pod下的节点 const isPod = () => { - // 父节点有两个pod,一种是scope_id为1,另一种是scope_id为3下的target_id为21(Kubernetes下的node) - return ( - activeCol?.scope_id === 1 || - (activeCol?.scope_id === 3 && activeCol?.target_id === 21) - ); + // 父节点有两个pod,一种是scope_id为1,另一种是通过接口查询name为pod + return activeCol?.scope_id === 1 || targetName === 'pod'; }; useEffect(() => { @@ -270,6 +289,7 @@ const NodeConfig: React.FC = (props) => { // 故障注入节点 if (activeCol?.exec_type === 'fault') { getFaultNodeFields?.run({ id: activeCol?.exec_id }); + getFaultNodeDetail?.run({ targetId: activeCol?.target_id }); } // 流量注入 if (activeCol?.exec_type === 'flow') { @@ -280,10 +300,133 @@ const NodeConfig: React.FC = (props) => { } }, [activeCol?.uuid]); + // 持续时长失去焦点的操作 + const handleDurationBlur = () => { + const durationValue = form.getFieldValue('duration'); + let newDuration = durationValue; + // 输入框有值且没有单位时拼接单位 + if (durationValue && !durationValue?.includes('s')) { + newDuration = `${durationValue}s`; + form.setFieldValue('duration', newDuration); + } + // 修改后更新时长 + handleEditNode('duration', newDuration); + }; + + // 攻击范围下不同节点渲染不同 + const attackRangeRender = () => { + // 父节点为node时 + if (isNode()) { + return ( + <> + + + + + + + + + + + ); + } + // 父节点为pod时 + if (isPod()) { + return ( + <> + + { + setKubernetesNamespace(val); + form.setFieldValue(['exec_range', 'target_name'], undefined); + }} + /> + + + + + + + + + ); + } + // 父节点为deployment时 + if (targetName === 'deployment') { + return ( + <> + + { + setKubernetesNamespace(val); + form.setFieldValue(['exec_range', 'target_name'], undefined); + }} + /> + + + + + + ); + } + }; + useEffect(() => { const initSecond = formatDuration(activeCol?.duration); form.setFieldsValue({ - duration: initSecond, + duration: `${initSecond}s`, }); }, [activeCol?.duration]); @@ -373,118 +516,76 @@ const NodeConfig: React.FC = (props) => { >
+ {/* 为度量引擎或流量注入时不展示 */} {activeCol?.exec_type !== 'flow' && activeCol?.exec_type !== 'measure' && ( - { - if ((value || value === 0) && value <= 0) { - return Promise.reject( - `${ - activeCol?.exec_type === 'wait' - ? intl.formatMessage({ id: 'waitTime' }) - : intl.formatMessage({ id: 'duration' }) - } ${intl.formatMessage({ id: 'limit' })}`, - ); - } - return Promise.resolve(); + <> + + + +
+ {intl.formatMessage({ id: 'commonParameters' })} +
+ { + if ((value || value === 0) && value <= 0) { + return Promise.reject( + `${ + activeCol?.exec_type === 'wait' + ? intl.formatMessage({ id: 'waitTime' }) + : intl.formatMessage({ id: 'duration' }) + } ${intl.formatMessage({ id: 'limit' })}`, + ); + } + return Promise.resolve(); + }, }, - }, - ]} - > - - + ]} + > + { + handleDurationBlur(); + }} + style={{ width: '100%' }} + /> +
+ )} {/* 等待时长类型没有以下配置信息 */} {activeCol?.exec_type !== 'wait' && ( <> {/* 动态表单部分 */} - - {/* 节点类型为node或pod时才展示 */} - {(isNode() || isPod()) && ( + {/* 节点父类型为node或pod 或deployment时才展示 攻击范围 */} + {(isNode() || isPod() || targetName === 'deployment') && ( <>
{intl.formatMessage({ id: 'attackRange' })}
- {/* 一级节点为node时不展示Namespace */} - {!isNode() && ( - - { - setKubernetesNamespace(val); - }} - /> - - )} - - - - - - {isNode() ? ( - - ) : ( - - )} - - {/* 一级节点为node时展示,node的id为2 */} - {isNode() && ( - <> - - - - - )} + {attackRangeRender()} )} diff --git a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/index.tsx b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/index.tsx index 4f3a908..e066f05 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/index.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/AddExperiment/index.tsx @@ -164,6 +164,7 @@ const AddExperiment = () => { target_id, exec_type, name, + exec_name, measure_range, flow_range, } = item; @@ -175,8 +176,10 @@ const AddExperiment = () => { ...exec_range, target_name: target_name || undefined, }; + let newExecName = exec_name; if (exec_type === 'flow' || exec_type === 'measure') { newExecRange = undefined; + newExecName = undefined; } if (measure_range) { measure_range.duration = duration; @@ -186,6 +189,7 @@ const AddExperiment = () => { } return { name, + exec_name: newExecName, args_value, exec_range: newExecRange, exec_id, diff --git a/chaosmeta-platform-frontend/src/pages/Space/Experiment/ExperimentList.tsx b/chaosmeta-platform-frontend/src/pages/Space/Experiment/ExperimentList.tsx index 47451d0..84c90a1 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/Experiment/ExperimentList.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/Experiment/ExperimentList.tsx @@ -138,6 +138,7 @@ const ExperimentList: React.FC = () => { end_time, time_search_field: timeType, namespace_id: history?.location?.query?.spaceId as string, + time_type: end_time ? 'range' : undefined, }; queryByPage.run(queryParam); }; @@ -332,8 +333,9 @@ const ExperimentList: React.FC = () => { ), width: 180, - sorter: true, - render: (record: any) => { + // sorter: true, + dataIndex: 'last_instance', + render: (text: string, record: any) => { const statusTemp: any = experimentStatus.filter( (item) => item.value === record?.status, )[0]; diff --git a/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/ArrangeInfoShow.tsx b/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/ArrangeInfoShow.tsx index 9411fb5..fe3ea62 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/ArrangeInfoShow.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/ArrangeInfoShow.tsx @@ -1,4 +1,3 @@ -import DynamicForm from '@/components/DynamicForm'; import ShowText from '@/components/ShowText'; import { arrangeNodeTypeColors, @@ -12,15 +11,21 @@ import { queryFlowNodeFields, queryMeasureNodeFields, } from '@/services/chaosmeta/ExperimentController'; +import { queryFaultNodeDetail } from '@/services/chaosmeta/KubernetesController'; import { formatDuration, getIntlLabel, handleTimeTransform, } from '@/utils/format'; -import { ZoomInOutlined, ZoomOutOutlined } from '@ant-design/icons'; +import { + CheckCircleFilled, + ZoomInOutlined, + ZoomOutOutlined, +} from '@ant-design/icons'; import { getLocale, history, useIntl, useRequest } from '@umijs/max'; import { Form, Space, Spin } from 'antd'; import { useEffect, useState } from 'react'; +import DynamicFormRender from '../AddExperiment/components/DynamicFormRender'; import { ArrangeWrap, DroppableCol, DroppableRow } from './style'; interface IProps { @@ -29,6 +34,7 @@ interface IProps { // 以下都是结果详情需要的 isResult?: boolean; getExperimentArrangeNodeDetail?: any; + setCurNodeDetail?: any; } const ArrangeInfoShow: React.FC = (props) => { const { @@ -36,6 +42,7 @@ const ArrangeInfoShow: React.FC = (props) => { curExecSecond, isResult, getExperimentArrangeNodeDetail, + setCurNodeDetail, } = props; // 当前占比 const [curProportion, setCurProportion] = useState(100); @@ -48,7 +55,8 @@ const ArrangeInfoShow: React.FC = (props) => { const [fieldList, setFieldList] = useState([]); const [configForm] = Form.useForm(); const intl = useIntl(); - + // 用于判断当前节点是否为 kubernetes node或 kubernetes pod节点下 + const [targetName, setTargetName] = useState(''); /** * 故障节点 - 查询节点表单配置信息 */ @@ -88,6 +96,19 @@ const ArrangeInfoShow: React.FC = (props) => { }, }); + /** + * 根据targetid获取该节点信息,用于判断该节点是否位于node或pod下 + */ + const getFaultNodeDetail = useRequest(queryFaultNodeDetail, { + manual: true, + formatResult: (res) => res, + onSuccess: (res: any) => { + if (res?.code === 200) { + setTargetName(res?.data?.name); + } + }, + }); + /** * @description: 处理函数,计算二级列表中所有子项的总时长并更新到总时长上 */ @@ -136,6 +157,9 @@ const ArrangeInfoShow: React.FC = (props) => { setActiveCol({ ...el, }); + if (setCurNodeDetail) { + setCurNodeDetail(el); + } return; } // 获取节点动态表单部分 @@ -147,6 +171,7 @@ const ArrangeInfoShow: React.FC = (props) => { } if (el?.exec_type === 'fault') { getFaultNodeFields?.run({ id: el?.exec_id }); + getFaultNodeDetail?.run({ targetId: el?.target_id }); } // 结果详情中需要通过接口获取节点信息,实验详情中则在详情中直接返回了 if (isResult) { @@ -276,13 +301,21 @@ const ArrangeInfoShow: React.FC = (props) => { {curDuration}s {isError && ( - + )} + {el?.status === 'Succeeded' && ( + + + + )} ) : ( <> @@ -303,20 +336,22 @@ const ArrangeInfoShow: React.FC = (props) => { // 是否为node下的节点 const isNode = () => { - // 父节点有两个node,一种是scope_id为2,另一种是scope_id为3下的target_id为23(Kubernetes下的node) - return ( - activeCol?.scope_id === 2 || - (activeCol?.scope_id === 3 && activeCol?.target_id === 23) - ); + // 只有故障节点下的才有可能有node节点 + if (activeCol?.exec_type !== 'fault') { + return false; + } + // 父节点有两个node,一种是scope_id为2,另一种是通过接口查询name为node + return activeCol?.scope_id === 2 || targetName === 'node'; }; // 是否为pod下的节点 const isPod = () => { - // 父节点有两个pod,一种是scope_id为1,另一种是scope_id为3下的target_id为21(Kubernetes下的node) - return ( - activeCol?.scope_id === 1 || - (activeCol?.scope_id === 3 && activeCol?.target_id === 21) - ); + // 只有故障节点下的才有可能有node节点 + if (activeCol?.exec_type !== 'fault') { + return false; + } + // 父节点有两个pod,一种是scope_id为1,另一种是通过接口查询name为pod + return activeCol?.scope_id === 1 || targetName === 'pod'; }; /** @@ -359,9 +394,75 @@ const ArrangeInfoShow: React.FC = (props) => { }); }, [curProportion]); + // 攻击范围下不同节点渲染不同 + const attackRangeRender = () => { + // 父节点为node时 + if (isNode()) { + return ( + <> + + + + + + + + + + + ); + } + + // 父节点为pod时 + if (isPod()) { + return ( + <> + + + + + + + + + + + ); + } + // 父节点为deployment时 + if (targetName === 'deployment') { + return ( + <> + + + + + + + + ); + } + }; + useEffect(() => { handleAddTimeAxis(arrangeList); }, [arrangeList]); + useEffect(() => { handleTotalSecond(); }, []); @@ -427,63 +528,45 @@ const ArrangeInfoShow: React.FC = (props) => {
{activeCol?.exec_type !== 'flow' && activeCol?.exec_type !== 'measure' && ( - - - + <> + + + +
+ {intl.formatMessage({ id: 'commonParameters' })} +
+ + + + )} {activeCol?.exec_type !== 'wait' && ( <> {/* 动态表单部分 */} - - {/* 攻击范围为流量或度量时不展示 */} - {(isNode() || isPod()) && ( + {/* 节点父类型为node或pod 或deployment时才展示 攻击范围 */} + {(isNode() || isPod() || targetName === 'deployment') && ( <>
{intl.formatMessage({ id: 'attackRange' })}
{/* node下的节点时不展示 */} - {!isNode() && ( - - - - )} - - - - - - - - {/* node下的节点时才展示 */} - {isNode() && ( - - - - )} + {attackRangeRender()} )} diff --git a/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/style.ts b/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/style.ts index 4884886..39449d6 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/style.ts +++ b/chaosmeta-platform-frontend/src/pages/Space/ExperimentDetail/style.ts @@ -37,7 +37,7 @@ export const ArrangeWrap = styled.div<{ border: 1px solid rgba(0, 10, 26, 0.1); position: relative; .arrange { - min-height: calc(100vh - 380px); + height: calc(100vh - 380px); padding-left: 38px; margin-bottom: 40px; flex: 1; @@ -72,7 +72,6 @@ export const ArrangeWrap = styled.div<{ .time-item { position: relative; color: rgba(0, 10, 26, 0.47); - font-weight: 400; flex-shrink: 0; background-color: #fafafc; @@ -90,6 +89,8 @@ export const ArrangeWrap = styled.div<{ } } .info { + overflow: auto; + height: calc(100vh - 380px); width: 230px; padding: 16px; right: 0; @@ -213,6 +214,10 @@ export const DroppableCol = styled.div<{ if (props?.$nodeStutas === 'Failed' || props?.$nodeStutas === 'error') { border = '2px solid #FF4D4F'; } + if(props?.$nodeStutas === 'Succeeded') { + border = '2px solid #52c41a'; + + } if (props?.$activeState) { border = '2px solid #597EF7'; } @@ -236,10 +241,10 @@ export const DroppableCol = styled.div<{ justify-content: space-between; padding-right: 10px; } - .error-icon { + .tip-icon { position: absolute; top: 6px; - right: 0; + right: 2px; } } `; diff --git a/chaosmeta-platform-frontend/src/pages/Space/ExperimentResult/index.tsx b/chaosmeta-platform-frontend/src/pages/Space/ExperimentResult/index.tsx index 4416e97..5d26aed 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/ExperimentResult/index.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/ExperimentResult/index.tsx @@ -107,7 +107,7 @@ const ExperimentResult: React.FC = () => { end_time = formatTime(time[1]?.format()); } // 默认使用更新时间倒序 - const sort = params?.sort || '-update_time'; + const sort = params?.sort || '-create_time'; const queryParam = { sort, name, @@ -120,6 +120,7 @@ const ExperimentResult: React.FC = () => { start_time, end_time, namespace_id: history?.location?.query?.spaceId as string, + time_type: end_time ? 'range' : undefined, }; queryByPage.run(queryParam); }; diff --git a/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/ShowLog.tsx b/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/ShowLog.tsx index 1310ec9..f7c6f93 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/ShowLog.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/ShowLog.tsx @@ -1,7 +1,8 @@ -import ReactMarkdown from 'react-markdown'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { atomOneDark } from 'react-syntax-highlighter/dist/esm/styles/hljs'; import { LogConainer } from './style'; +// 编辑器相关, 顺序不能变更 +import AceEditor from 'react-ace'; +import 'ace-builds/src-noconflict/mode-java'; +import 'ace-builds/src-noconflict/theme-monokai'; interface Props { message?: string; @@ -18,30 +19,30 @@ const ShowLog = (props: Props) => { */} -
- - {children as string} - - ); - }, - }} - > - {` - ${markdown} - `} - -
+ ); }; diff --git a/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/index.tsx b/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/index.tsx index 4fd3675..fe3f921 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/index.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/ExperimentResultDetail/index.tsx @@ -249,6 +249,7 @@ const AddExperiment = () => { curExecSecond={formatDuration(curExecSecond)} isResult getExperimentArrangeNodeDetail={getExperimentArrangeNodeDetail} + setCurNodeDetail={setCurNodeDetail} /> {/* 日志信息 */}
diff --git a/chaosmeta-platform-frontend/src/pages/Space/SpaceOverview/ExperimentalOverview/index.tsx b/chaosmeta-platform-frontend/src/pages/Space/SpaceOverview/ExperimentalOverview/index.tsx index 6c5995c..2a1bc49 100644 --- a/chaosmeta-platform-frontend/src/pages/Space/SpaceOverview/ExperimentalOverview/index.tsx +++ b/chaosmeta-platform-frontend/src/pages/Space/SpaceOverview/ExperimentalOverview/index.tsx @@ -81,6 +81,81 @@ export default () => { }, }); + /** + * 日期转换 + * @returns + */ + const transformTime = (value?: string) => { + let start_time, end_time; + const curTime = value || timeType; + if (curTime === '7day') { + start_time = formatTime(dayjs().subtract(6, 'd').startOf('day')); + end_time = formatTime(dayjs().endOf('day')); + } + if (curTime === '30day') { + start_time = formatTime(dayjs().subtract(30, 'd').startOf('day')); + end_time = formatTime(dayjs().endOf('day')); + } + return { + start_time, + end_time, + }; + }; + + // 最近运行实验结果接口 + const handleExperimentResultList = (params: { + curTime?: string; + pageSize?: number; + pageIndex?: number; + }) => { + const { curTime, pageIndex, pageSize } = params; + getExperimentResultList?.run({ + page: pageIndex || 1, + page_size: pageSize || 10, + time_search_field: 'create_time', + start_time: transformTime(curTime)?.start_time, + end_time: transformTime(curTime)?.end_time, + namespace_id: namespaceId, + time_type: 'range', + }); + }; + + // 实验列表 + const handleExperimentList = (params: { + curTime?: string; + pageSize?: number; + pageIndex?: number; + timeField?: string; + }) => { + const { curTime, pageIndex, pageSize } = params; + getExperimentList?.run({ + page: pageIndex || 1, + page_size: pageSize || 10, + time_search_field: 'create_time', + start_time: transformTime(curTime)?.start_time, + end_time: transformTime(curTime)?.end_time, + namespace_id: namespaceId, + time_type: 'range', + }); + }; + + // tab栏列表检索 + const handleTabSearch = (params?: { key?: string; curTime?: string }) => { + const { key, curTime } = params || {}; + const val = key || tabKey; + if (val === 'runningResult') { + handleExperimentResultList({ curTime }); + } + // 即将运行 + if (val === 'running') { + handleExperimentList({ timeField: 'next_exec', curTime }); + } + // 最近编辑 + if (val === 'update') { + handleExperimentList({ timeField: 'update_time', curTime }); + } + }; + /** * 停止实验 */ @@ -93,10 +168,9 @@ export default () => { onSuccess: (res, params) => { if (res?.code === 200) { message.success(`${params[0]?.name}实验已停止`); - getExperimentResultList?.run({ - page: experimentResultData?.page || 1, - page_size: experimentResultData?.pageSize || 10, - namespace_id: namespaceId, + handleExperimentResultList({ + pageIndex: experimentResultData?.page || 1, + pageSize: experimentResultData?.pageSize || 10, }); } }, @@ -197,7 +271,7 @@ export default () => { { history?.push({ - pathname: '/space/space/experiment-result', + pathname: '/space/experiment-result', query: { experimentId: record?.uuid, }, @@ -402,11 +476,7 @@ export default () => { }} onChange={(pagination: any) => { const { current, pageSize } = pagination; - getExperimentList?.run({ - page: current, - page_size: pageSize, - namespace_id: namespaceId, - }); + handleExperimentList({ pageIndex: current, pageSize }); }} scroll={{ x: 760 }} /> @@ -461,10 +531,9 @@ export default () => { }} onChange={(pagination: any) => { const { current, pageSize } = pagination; - getExperimentResultList?.run({ - page: current, - page_size: pageSize, - namespace_id: namespaceId, + handleExperimentResultList({ + pageSize: pageSize, + pageIndex: current, }); }} /> @@ -496,65 +565,6 @@ export default () => { }, ]; - /** - * 日期转换 - * @returns - */ - const transformTime = (value?: string) => { - let start_time, end_time; - const curTime = value || timeType; - if (curTime === '7day') { - start_time = formatTime(dayjs().subtract(6, 'd').startOf('day')); - end_time = formatTime(dayjs().endOf('day')); - } - if (curTime === '30day') { - start_time = formatTime(dayjs().subtract(30, 'd').startOf('day')); - end_time = formatTime(dayjs().endOf('day')); - } - return { - start_time, - end_time, - }; - }; - - // tab栏列表检索 - const handleTabSearch = (params?: { key?: string; curTime?: string }) => { - const { key, curTime } = params || {}; - const val = key || tabKey; - if (val === 'runningResult') { - getExperimentResultList?.run({ - page: 1, - page_size: 10, - time_search_field: 'create_time', - start_time: transformTime(curTime)?.start_time, - end_time: transformTime(curTime)?.end_time, - namespace_id: namespaceId, - }); - } - // 即将运行 - if (val === 'running') { - getExperimentList?.run({ - page: 1, - page_size: 10, - time_search_field: 'next_exec', - start_time: transformTime(curTime)?.start_time, - end_time: transformTime(curTime)?.end_time, - namespace_id: namespaceId, - }); - } - // 最近编辑 - if (val === 'update') { - getExperimentList?.run({ - page: 1, - page_size: 10, - time_search_field: 'update_time', - start_time: transformTime(curTime)?.start_time, - end_time: transformTime(curTime)?.end_time, - namespace_id: namespaceId, - }); - } - }; - useEffect(() => { handleTabSearch(); getSpaceOverview?.run({ diff --git a/chaosmeta-platform-frontend/src/services/chaosmeta/ExperimentController.ts b/chaosmeta-platform-frontend/src/services/chaosmeta/ExperimentController.ts index 3ca2150..487cb62 100644 --- a/chaosmeta-platform-frontend/src/services/chaosmeta/ExperimentController.ts +++ b/chaosmeta-platform-frontend/src/services/chaosmeta/ExperimentController.ts @@ -18,6 +18,7 @@ export async function queryExperimentList( start_time?: string; end_time?: string; namespace_id: string; + time_type?: string; }, options?: { [key: string]: any }, ) { @@ -140,6 +141,7 @@ export async function queryExperimentResultList( start_time?: string; end_time?: string; namespace_id: string; + time_type?: string }, options?: { [key: string]: any }, ) { diff --git a/chaosmeta-platform-frontend/src/services/chaosmeta/KubernetesController.ts b/chaosmeta-platform-frontend/src/services/chaosmeta/KubernetesController.ts index cba38af..22d15db 100644 --- a/chaosmeta-platform-frontend/src/services/chaosmeta/KubernetesController.ts +++ b/chaosmeta-platform-frontend/src/services/chaosmeta/KubernetesController.ts @@ -30,7 +30,7 @@ export async function queryNamespaceList( * @param options * @returns */ -export async function queryPodLIst( +export async function queryPodNameList( params?: { page?: number; page_size?: number; @@ -49,12 +49,12 @@ export async function queryPodLIst( } /** - * 获取podnode列表 + * 获取nodename列表 * @param params * @param options * @returns */ -export async function queryPodNodeList( +export async function queryNodeNameList( params?: { page?: number; page_size?: number; @@ -68,3 +68,47 @@ export async function queryPodNodeList( ...(options || {}), }); } + +/** + * 获取故障节点详情信息,属于那个target下的 + * @param params + * @param options + * @returns + */ +export async function queryFaultNodeDetail( + params?: { + targetId?: number; + }, + options?: { [key: string]: any }, +) { + return request( + `/chaosmeta/api/v1/injects/scopes/target/${params?.targetId}`, + { + method: 'GET', + params, + ...(options || {}), + }, + ); +} + + +/** + * 获取deploymentName列表 + * @param params + * @param options + * @returns + */ +export async function queryDeploymentNameList( + params?: { + page?: number; + page_size?: number; + namespace?: string; + }, + options?: { [key: string]: any }, +) { + return request(`/chaosmeta/api/v1/kubernetes/cluster/${envType}/namespace/${params?.namespace}/deployments`, { + method: 'GET', + params, + ...(options || {}), + }); +}