mirror of https://github.com/kubevela/velaux.git
Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
|
75fd406f23 | |
|
b557d60bad | |
|
8b9cd0f9fb | |
|
43d3af949d | |
|
29e490a51b | |
|
d4fa85d7d9 | |
|
07b7d795b4 | |
|
8bfb67708a | |
|
de8134046a | |
|
a2c793f6eb | |
|
e1c88692a6 | |
|
3563dbe98d |
14
package.json
14
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "valaux",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
|
@ -42,10 +42,6 @@
|
|||
"@alifd/meet-react": "^2.0.0",
|
||||
"@antv/g6": "4.3.11",
|
||||
"@b-design/ui": "^1.0.63",
|
||||
"@types/js-yaml": "^4.0.1",
|
||||
"@types/lodash": "^4.14.176",
|
||||
"@types/react": "^16.3.0",
|
||||
"@types/react-cookies": "^0.1.0",
|
||||
"ansi-to-react": "^6.1.6",
|
||||
"axios": "0.24.0",
|
||||
"diagram-maker": "^1.3.0",
|
||||
|
@ -72,7 +68,8 @@
|
|||
"redux": "4.1.2",
|
||||
"remark-gfm": "3.0.1",
|
||||
"tiny-pubsub": "^1.1.0",
|
||||
"tsx-control-statements": "4.1.1"
|
||||
"tsx-control-statements": "4.1.1",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
|
@ -87,6 +84,11 @@
|
|||
"@types/jest": "^26.0.24",
|
||||
"@types/react-copy-to-clipboard": "^5.0.2",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/js-yaml": "^4.0.1",
|
||||
"@types/lodash": "^4.14.176",
|
||||
"@types/react": "^16.3.0",
|
||||
"@types/react-cookies": "^0.1.0",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.0.0",
|
||||
"@typescript-eslint/parser": "^4.0.0",
|
||||
"@umijs/fabric": "2.8.1",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Dialog, Table, Card, Step, Loading, Button, Balloon } from '@b-design/ui';
|
||||
import { Dialog, Table, Card, Loading, Button, Balloon, Icon } from '@b-design/ui';
|
||||
import type { ApplicationStatus, Condition } from '../../interface/application';
|
||||
import Translation from '../../components/Translation';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
|
@ -17,37 +17,6 @@ type Props = {
|
|||
class StatusShow extends React.Component<Props> {
|
||||
render() {
|
||||
const { applicationStatus, onClose, loading, title } = this.props;
|
||||
const allConditions: Condition[] = [
|
||||
{ type: 'Parsed', status: 'False' },
|
||||
{ type: 'Revision', status: 'False' },
|
||||
{ type: 'Policy', status: 'False' },
|
||||
{ type: 'Render', status: 'False' },
|
||||
{ type: 'WorkflowFinished', status: 'False' },
|
||||
{ type: 'Ready', status: 'False' },
|
||||
];
|
||||
const getCurrent = (conditions?: Condition[]) => {
|
||||
let index = 0;
|
||||
conditions?.map((condition: Condition, i: number) => {
|
||||
if (condition.status == 'False') {
|
||||
index = i;
|
||||
}
|
||||
allConditions.map((c) => {
|
||||
if (c.type == condition.type) {
|
||||
c.status = condition.status;
|
||||
c.lastTransitionTime = condition.lastTransitionTime;
|
||||
c.reason = condition.reason;
|
||||
c.message = condition.message;
|
||||
}
|
||||
});
|
||||
if (condition.type == 'Deleting') {
|
||||
allConditions.push(condition);
|
||||
}
|
||||
});
|
||||
if (index == 0 && conditions) {
|
||||
return conditions.length;
|
||||
}
|
||||
return index;
|
||||
};
|
||||
return (
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
|
@ -107,26 +76,42 @@ class StatusShow extends React.Component<Props> {
|
|||
<Card
|
||||
locale={locale.Card}
|
||||
style={{ marginTop: '8px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Conditions</Translation>}
|
||||
>
|
||||
<Step current={getCurrent(applicationStatus?.conditions)}>
|
||||
{allConditions.map((condition) => {
|
||||
const content = condition.message ? (
|
||||
<Balloon
|
||||
trigger={
|
||||
<span style={{ cursor: 'pointer', color: '#1b58f4' }}>
|
||||
{condition.reason}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{condition.message}
|
||||
</Balloon>
|
||||
) : (
|
||||
condition.reason
|
||||
);
|
||||
return <Step.Item title={condition.type} content={content} />;
|
||||
})}
|
||||
</Step>
|
||||
<Table locale={locale.Table} dataSource={applicationStatus?.conditions}>
|
||||
<Table.Column
|
||||
width="150px"
|
||||
dataIndex="type"
|
||||
title={<Translation>Type</Translation>}
|
||||
/>
|
||||
<Table.Column dataIndex="status" title={<Translation>Status</Translation>} />
|
||||
|
||||
<Table.Column
|
||||
dataIndex="lastTransitionTime"
|
||||
title={<Translation>LastTransitionTime</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="reason"
|
||||
title={<Translation>Reason</Translation>}
|
||||
cell={(v: string, index: number, row: Condition) => {
|
||||
if (row.message) {
|
||||
return (
|
||||
<Balloon
|
||||
trigger={
|
||||
<span style={{ color: 'red', cursor: 'pointer' }}>
|
||||
{v} <Icon size={'xs'} type="question-circle" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{row.message}
|
||||
</Balloon>
|
||||
);
|
||||
}
|
||||
return <span>{v}</span>;
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Card>
|
||||
</If>
|
||||
<If condition={applicationStatus?.services}>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
.ui-schema-container,
|
||||
.group-container {
|
||||
.next-form-item {
|
||||
.next-form-item-control {
|
||||
width: 100%;
|
||||
}
|
||||
.next-form-item-label {
|
||||
label {
|
||||
color: #333;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Form, Input, Select, Field, Switch } from '@b-design/ui';
|
||||
import { Form, Input, Select, Field, Switch, Grid, Divider } from '@b-design/ui';
|
||||
import Translation from '../Translation';
|
||||
import type { UIParam, UIParamValidate } from '../../interface/application';
|
||||
import Group from '../../extends/Group';
|
||||
|
@ -9,8 +9,8 @@ import SecretSelect from '../../extends/SecretSelect';
|
|||
import SecretKeySelect from '../../extends/SecretKeySelect';
|
||||
import Structs from '../../extends/Structs';
|
||||
import CPUNumber from '../../extends/CPUNumber';
|
||||
import DiskNumber from '../../extends/DiskNumber';
|
||||
import MemoryNumber from '../../extends/MemoryNumber';
|
||||
import InnerGroup from '../../extends/InnerGroup';
|
||||
import K8sObjectsCode from '../../extends/K8sObjectsCode';
|
||||
import type { Rule } from '@alifd/field';
|
||||
import KV from '../../extends/KV';
|
||||
|
@ -18,55 +18,62 @@ import './index.less';
|
|||
import { checkImageName, replaceUrl } from '../../utils/common';
|
||||
import locale from '../../utils/locale';
|
||||
import HelmValues from '../../extends/HelmValues';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import i18n from 'i18next';
|
||||
|
||||
const { Col, Row } = Grid;
|
||||
|
||||
type Props = {
|
||||
inline?: boolean;
|
||||
id?: string;
|
||||
value?: any;
|
||||
uiSchema?: UIParam[];
|
||||
maxColSpan?: number;
|
||||
onChange?: (params: any) => void;
|
||||
registerForm?: (form: Field) => void;
|
||||
disableRenderRow?: boolean;
|
||||
mode: 'new' | 'edit';
|
||||
};
|
||||
|
||||
function converRule(validete: UIParamValidate) {
|
||||
function convertRule(validate: UIParamValidate) {
|
||||
const rules = [];
|
||||
if (!validete) {
|
||||
if (!validate) {
|
||||
return [];
|
||||
}
|
||||
if (validete.required) {
|
||||
if (validate.required) {
|
||||
rules.push({
|
||||
required: true,
|
||||
message: 'This field is required.',
|
||||
});
|
||||
}
|
||||
if (validete.min) {
|
||||
if (validate.min != undefined) {
|
||||
rules.push({
|
||||
min: validete.min,
|
||||
message: 'Enter a number greater than ' + validete.min,
|
||||
min: validate.min,
|
||||
message: 'Enter a number greater than ' + validate.min,
|
||||
});
|
||||
}
|
||||
if (validete.max) {
|
||||
if (validate.max != undefined) {
|
||||
rules.push({
|
||||
max: validete.max,
|
||||
message: 'Enter a number less than ' + validete.max,
|
||||
max: validate.max,
|
||||
message: 'Enter a number less than ' + validate.max,
|
||||
});
|
||||
}
|
||||
if (validete.minLength) {
|
||||
if (validate.minLength != undefined) {
|
||||
rules.push({
|
||||
minLength: validete.minLength,
|
||||
message: `Enter a minimum of ${validete.minLength} characters.`,
|
||||
minLength: validate.minLength,
|
||||
message: `Enter a minimum of ${validate.minLength} characters.`,
|
||||
});
|
||||
}
|
||||
if (validete.maxLength) {
|
||||
if (validate.maxLength != undefined) {
|
||||
rules.push({
|
||||
maxLength: validete.maxLength,
|
||||
message: `Enter a maximum of ${validete.maxLength} characters.`,
|
||||
maxLength: validate.maxLength,
|
||||
message: `Enter a maximum of ${validate.maxLength} characters.`,
|
||||
});
|
||||
}
|
||||
if (validete.pattern) {
|
||||
if (validate.pattern) {
|
||||
rules.push({
|
||||
pattern: validete.pattern,
|
||||
message: `Please enter a value that conforms to the specification. ` + validete.pattern,
|
||||
pattern: new RegExp(validate.pattern),
|
||||
message: `Please enter a value that conforms to the specification. ` + validate.pattern,
|
||||
});
|
||||
}
|
||||
return rules;
|
||||
|
@ -74,6 +81,7 @@ function converRule(validete: UIParamValidate) {
|
|||
|
||||
type State = {
|
||||
secretKeys?: string[];
|
||||
advanced: boolean;
|
||||
};
|
||||
|
||||
class UISchema extends Component<Props, State> {
|
||||
|
@ -94,6 +102,7 @@ class UISchema extends Component<Props, State> {
|
|||
}
|
||||
this.state = {
|
||||
secretKeys: [],
|
||||
advanced: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -105,6 +114,10 @@ class UISchema extends Component<Props, State> {
|
|||
this.registerForm[key] = form;
|
||||
};
|
||||
|
||||
onChangeAdvanced = (advanced: boolean) => {
|
||||
this.setState({ advanced: advanced });
|
||||
};
|
||||
|
||||
setValues = () => {
|
||||
const { value } = this.props;
|
||||
if (value) {
|
||||
|
@ -126,11 +139,15 @@ class UISchema extends Component<Props, State> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { uiSchema, inline } = this.props;
|
||||
const { advanced } = this.state;
|
||||
const { uiSchema, inline, maxColSpan, disableRenderRow, value } = this.props;
|
||||
if (!uiSchema) {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
let onlyShowRequired = false;
|
||||
if (uiSchema.length > 5) {
|
||||
onlyShowRequired = true;
|
||||
}
|
||||
const items = uiSchema.map((param) => {
|
||||
const init = this.form.init;
|
||||
const required = param.validate && param.validate.required;
|
||||
|
@ -138,6 +155,10 @@ class UISchema extends Component<Props, State> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (onlyShowRequired && !param.validate.required && !advanced) {
|
||||
return;
|
||||
}
|
||||
|
||||
const validator = (rule: Rule, v: any, callback: (error?: string) => void) => {
|
||||
if (this.registerForm[param.jsonKey]) {
|
||||
this.registerForm[param.jsonKey].validate((errors: any) => {
|
||||
|
@ -153,12 +174,22 @@ class UISchema extends Component<Props, State> {
|
|||
}
|
||||
};
|
||||
|
||||
let description = param.description;
|
||||
if (description) {
|
||||
description = i18n.t(description);
|
||||
}
|
||||
let label = param.label;
|
||||
if (label) {
|
||||
label = i18n.t(label);
|
||||
}
|
||||
const initValue = (value && value[param.jsonKey]) || param.validate.defaultValue;
|
||||
const disableEdit = (param.validate.immutable && this.props.mode == 'edit') || false;
|
||||
const getGroup = (children: React.ReactNode) => {
|
||||
return (
|
||||
<Group
|
||||
hasToggleIcon
|
||||
description={<Translation>{param.description || ''}</Translation>}
|
||||
title={<Translation>{param.label || ''}</Translation>}
|
||||
description={description}
|
||||
title={label}
|
||||
closed={true}
|
||||
required={param.validate && param.validate.required}
|
||||
field={this.form}
|
||||
|
@ -170,279 +201,369 @@ class UISchema extends Component<Props, State> {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Form.Item required={required} disabled={param.disable} key={param.jsonKey}>
|
||||
<Form.Item required={required} disabled={disableEdit} key={param.jsonKey}>
|
||||
{children}
|
||||
</Form.Item>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
const { value } = this.props;
|
||||
const initValue = param.validate.defaultValue || (value && value[param.jsonKey]);
|
||||
switch (param.uiType) {
|
||||
case 'Switch':
|
||||
const switchResult = init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
});
|
||||
return (
|
||||
<Form.Item
|
||||
className="switch-container"
|
||||
required={required}
|
||||
key={param.jsonKey}
|
||||
label={<span title={param.description}>{param.label}</span>}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
>
|
||||
<Switch
|
||||
id={switchResult.id}
|
||||
onChange={switchResult.onChange}
|
||||
checked={switchResult.value ? true : false}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Input':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={param.label}
|
||||
key={param.jsonKey}
|
||||
help={
|
||||
<div dangerouslySetInnerHTML={{ __html: replaceUrl(param.description || '') }} />
|
||||
|
||||
const item = () => {
|
||||
switch (param.uiType) {
|
||||
case 'Switch':
|
||||
const getDefaultSwtichValue = (validate: any) => {
|
||||
if (validate.required === true) {
|
||||
return false;
|
||||
}
|
||||
disabled={param.disable}
|
||||
>
|
||||
<Input
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Password':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={param.label}
|
||||
key={param.jsonKey}
|
||||
help={
|
||||
<div dangerouslySetInnerHTML={{ __html: replaceUrl(param.description || '') }} />
|
||||
}
|
||||
disabled={param.disable}
|
||||
>
|
||||
<Input
|
||||
htmlType="password"
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Select':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={param.label}
|
||||
key={param.jsonKey}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
dataSource={param.validate && param.validate.options}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Number':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={param.label}
|
||||
key={param.jsonKey}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
>
|
||||
<Input
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
htmlType="number"
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'ImageInput':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={param.label}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<ImageInput
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: checkImageName,
|
||||
message: 'Please enter a valid image name',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'KV':
|
||||
const children = (
|
||||
<KV
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
key={param.jsonKey}
|
||||
additional={param.additional}
|
||||
additionalParameter={param.additionalParameter}
|
||||
/>
|
||||
);
|
||||
return getGroup(children);
|
||||
case 'HelmValues':
|
||||
return getGroup(
|
||||
<HelmValues
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
key={param.jsonKey}
|
||||
additional={param.additional}
|
||||
additionalParameter={param.additionalParameter}
|
||||
/>,
|
||||
);
|
||||
case 'Strings':
|
||||
return getGroup(
|
||||
<Strings
|
||||
key={param.jsonKey}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
case 'SecretSelect':
|
||||
return (
|
||||
<Form.Item
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
required={required}
|
||||
label={param.label}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<SecretSelect
|
||||
setKeys={(keys: string[]) => {
|
||||
this.setState({ secretKeys: keys });
|
||||
}}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: param.validate.defaultValue || this.props.value?.name,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'SecretKeySelect':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={param.label}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<SecretKeySelect
|
||||
secretKeys={this.state.secretKeys}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: param.validate.defaultValue || this.props.value?.key,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'CPUNumber':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={param.label}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<CPUNumber
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: param.validate.required,
|
||||
min: 0,
|
||||
message: 'Please enter a valid cpu request number',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'MemoryNumber':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={param.label}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<MemoryNumber
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: param.validate.required,
|
||||
min: 0,
|
||||
message: 'Please enter a valid memory request number',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Group':
|
||||
if (param.subParameters && param.subParameters.length > 0) {
|
||||
};
|
||||
const switchResult = init(param.jsonKey, {
|
||||
initValue: initValue || getDefaultSwtichValue(param.validate),
|
||||
rules: convertRule(param.validate),
|
||||
});
|
||||
return (
|
||||
<Group
|
||||
<Form.Item
|
||||
className="switch-container"
|
||||
required={required}
|
||||
key={param.jsonKey}
|
||||
hasToggleIcon
|
||||
description={<Translation>{param.description || ''}</Translation>}
|
||||
title={<Translation>{param.label || ''}</Translation>}
|
||||
closed={true}
|
||||
required={param.validate && param.validate.required}
|
||||
field={this.form}
|
||||
jsonKey={param.jsonKey || ''}
|
||||
propertyValue={this.props.value}
|
||||
onChange={(values) => {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(values);
|
||||
}
|
||||
}}
|
||||
label={<span title={description}>{label}</span>}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
>
|
||||
<Switch
|
||||
disabled={disableEdit}
|
||||
id={switchResult.id}
|
||||
onChange={switchResult.onChange}
|
||||
size="medium"
|
||||
checked={switchResult.value ? true : false}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Input':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={label}
|
||||
key={param.jsonKey}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
>
|
||||
<Input
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Password':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={label}
|
||||
key={param.jsonKey}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
>
|
||||
<Input
|
||||
disabled={disableEdit}
|
||||
htmlType="password"
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Select':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={label}
|
||||
key={param.jsonKey}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
>
|
||||
<Select
|
||||
disabled={disableEdit}
|
||||
locale={locale.Select}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
dataSource={param.validate && param.validate.options}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Number':
|
||||
return (
|
||||
<Form.Item
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
required={required}
|
||||
label={label}
|
||||
key={param.jsonKey}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
>
|
||||
<Input
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
htmlType="number"
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'ImageInput':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={label}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<ImageInput
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: checkImageName,
|
||||
message: 'Please enter a valid image name',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'KV':
|
||||
const children = (
|
||||
<KV
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
key={param.jsonKey}
|
||||
additional={param.additional}
|
||||
additionalParameter={param.additionalParameter}
|
||||
/>
|
||||
);
|
||||
return getGroup(children);
|
||||
case 'HelmValues':
|
||||
return getGroup(
|
||||
<HelmValues
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
key={param.jsonKey}
|
||||
additional={param.additional}
|
||||
additionalParameter={param.additionalParameter}
|
||||
/>,
|
||||
);
|
||||
case 'Strings':
|
||||
return getGroup(
|
||||
<Strings
|
||||
disabled={disableEdit}
|
||||
key={param.jsonKey}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
case 'SecretSelect':
|
||||
return (
|
||||
<Form.Item
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
required={required}
|
||||
label={label}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
disabled={disableEdit}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<SecretSelect
|
||||
disabled={disableEdit}
|
||||
setKeys={(keys: string[]) => {
|
||||
this.setState({ secretKeys: keys });
|
||||
}}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: this.props.value?.name || param.validate.defaultValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'SecretKeySelect':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
labelAlign={inline ? 'inset' : 'left'}
|
||||
label={label}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
disabled={disableEdit}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<SecretKeySelect
|
||||
disabled={disableEdit}
|
||||
secretKeys={this.state.secretKeys}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: this.props.value?.key || param.validate.defaultValue,
|
||||
rules: convertRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'CPUNumber':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={label}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
disabled={disableEdit}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<CPUNumber
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: param.validate.required,
|
||||
min: 0,
|
||||
message: 'Please enter a valid cpu request number',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'MemoryNumber':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={label}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
disabled={disableEdit}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<MemoryNumber
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: param.validate.required,
|
||||
min: 0,
|
||||
message: 'Please enter a valid memory request number',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'DiskNumber':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={label}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
disabled={disableEdit}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<DiskNumber
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: param.validate.required,
|
||||
min: 0,
|
||||
message: 'Please enter a valid disk size',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Group':
|
||||
if (param.subParameters && param.subParameters.length > 0) {
|
||||
return (
|
||||
<Group
|
||||
key={param.jsonKey}
|
||||
hasToggleIcon
|
||||
description={
|
||||
<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />
|
||||
}
|
||||
title={label}
|
||||
closed={true}
|
||||
required={param.validate && param.validate.required}
|
||||
field={this.form}
|
||||
jsonKey={param.jsonKey || ''}
|
||||
propertyValue={this.props.value}
|
||||
onChange={(values) => {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(values);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<UISchema
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
validator: validator,
|
||||
},
|
||||
],
|
||||
})}
|
||||
registerForm={(form: Field) => {
|
||||
this.onRegisterForm(param.jsonKey, form);
|
||||
}}
|
||||
uiSchema={param.subParameters}
|
||||
mode={this.props.mode}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
case 'Structs':
|
||||
if (param.subParameters && param.subParameters.length > 0) {
|
||||
return getGroup(
|
||||
<Structs
|
||||
key={param.jsonKey}
|
||||
label={label}
|
||||
param={param.subParameters}
|
||||
parameterGroupOption={param.subParameterGroupOption}
|
||||
registerForm={(form: Field) => {
|
||||
this.onRegisterForm(param.jsonKey, form);
|
||||
}}
|
||||
mode={this.props.mode}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
validator: validator,
|
||||
message: `Please check ${label} config`,
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
case 'Ignore':
|
||||
if (param.subParameters && param.subParameters.length > 0) {
|
||||
const itemCount = param.subParameters?.filter((p) => !p.disable).length || 1;
|
||||
return (
|
||||
<UISchema
|
||||
uiSchema={param.subParameters}
|
||||
registerForm={(form: Field) => {
|
||||
this.onRegisterForm(param.jsonKey, form);
|
||||
}}
|
||||
inline={inline}
|
||||
maxColSpan={24 / itemCount}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
|
@ -451,100 +572,77 @@ class UISchema extends Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
registerForm={(form: Field) => {
|
||||
this.onRegisterForm(param.jsonKey, form);
|
||||
}}
|
||||
uiSchema={param.subParameters}
|
||||
mode={this.props.mode}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
case 'InnerGroup':
|
||||
return (
|
||||
<InnerGroup
|
||||
key={param.jsonKey}
|
||||
uiSchema={param.subParameters}
|
||||
title={param.label}
|
||||
description={param.description}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: converRule(param.validate),
|
||||
})}
|
||||
/>
|
||||
);
|
||||
case 'Structs':
|
||||
if (param.subParameters && param.subParameters.length > 0) {
|
||||
return getGroup(
|
||||
<Structs
|
||||
key={param.jsonKey}
|
||||
param={param.subParameters}
|
||||
parameterGroupOption={param.subParameterGroupOption}
|
||||
registerForm={(form: Field) => {
|
||||
this.onRegisterForm(param.jsonKey, form);
|
||||
}}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
validator: validator,
|
||||
message: `Please check ${param.label} config`,
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
case 'Ignore':
|
||||
if (param.subParameters && param.subParameters.length > 0) {
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
case 'K8sObjectsCode':
|
||||
return (
|
||||
<UISchema
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={label}
|
||||
help={<div dangerouslySetInnerHTML={{ __html: replaceUrl(description || '') }} />}
|
||||
disabled={disableEdit}
|
||||
key={param.jsonKey}
|
||||
uiSchema={param.subParameters}
|
||||
registerForm={(form: Field) => {
|
||||
this.onRegisterForm(param.jsonKey, form);
|
||||
}}
|
||||
inline={inline}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
validator: validator,
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
>
|
||||
<K8sObjectsCode
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: param.validate.required,
|
||||
message: 'Please enter a valid kubernetes resource yaml code',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
case 'K8sObjectsCode':
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={param.label}
|
||||
help={param.description}
|
||||
disabled={param.disable}
|
||||
key={param.jsonKey}
|
||||
>
|
||||
<K8sObjectsCode
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: param.validate.required,
|
||||
message: 'Please enter a valid kubernetes resource yaml code',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
};
|
||||
let colSpan = 24;
|
||||
if (maxColSpan) {
|
||||
colSpan = maxColSpan;
|
||||
if (maxColSpan * uiSchema.length < 24) {
|
||||
}
|
||||
}
|
||||
if (param.style?.colSpan) {
|
||||
colSpan = param.style?.colSpan;
|
||||
}
|
||||
return (
|
||||
<Col key={param.jsonKey} span={colSpan} style={{ padding: '0 4px' }}>
|
||||
{item()}
|
||||
</Col>
|
||||
);
|
||||
});
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
fixedSpan: 10,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 14,
|
||||
},
|
||||
};
|
||||
return (
|
||||
<Form field={this.form} className="ui-schema-container">
|
||||
{items}
|
||||
<If condition={disableRenderRow}>{items}</If>
|
||||
<If condition={!disableRenderRow}>
|
||||
<Row wrap={true}>{items}</Row>
|
||||
<If condition={onlyShowRequired}>
|
||||
<Divider />
|
||||
<Form {...formItemLayout} style={{ width: '100%' }} fullWidth={true}>
|
||||
<Form.Item
|
||||
labelAlign="left"
|
||||
colon={true}
|
||||
label={<Translation>Advanced Parameters</Translation>}
|
||||
labelWidth="200px"
|
||||
>
|
||||
<Switch onChange={this.onChangeAdvanced} size="small" checked={advanced} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</If>
|
||||
</If>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
@primaryColor: #0064c8;
|
||||
@whiteColor: white;
|
||||
@blackColor: black;
|
||||
.spection-group-container {
|
||||
padding: 8px;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
visibility: visible;
|
||||
&:hover {
|
||||
border: 1px solid @primaryColor;
|
||||
}
|
||||
.spection-group-title-container {
|
||||
position: relative;
|
||||
padding: 0 10px;
|
||||
font-size: 14px;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.12s;
|
||||
.icon-toggle {
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: 10px;
|
||||
}
|
||||
.icon-delete {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
}
|
||||
.next-icon-arrow-up {
|
||||
&::before {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.array-item-group-box {
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 5px;
|
||||
transition: height 0.3s linear;
|
||||
}
|
||||
.array-item-group-box.disable {
|
||||
height: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
import React from 'react';
|
||||
import { Icon, Loading, Grid } from '@b-design/ui';
|
||||
import './index.less';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
children?: React.ReactNode;
|
||||
loading?: boolean;
|
||||
labelTitle: string | React.ReactElement;
|
||||
delete: (id: string) => void;
|
||||
};
|
||||
|
||||
type State = {
|
||||
closed: boolean | undefined;
|
||||
};
|
||||
|
||||
class ArrayItemGroup extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
closed: true,
|
||||
};
|
||||
}
|
||||
|
||||
toggleShowClass = () => {
|
||||
const { closed } = this.state;
|
||||
this.setState({
|
||||
closed: !closed,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, labelTitle, loading } = this.props;
|
||||
const { closed } = this.state;
|
||||
const { Col, Row } = Grid;
|
||||
return (
|
||||
<Loading visible={loading || false} style={{ width: '100%' }}>
|
||||
<div className="spection-group-container">
|
||||
<div className="spection-group-title-container">
|
||||
<Row>
|
||||
<Col span={'21'}>{labelTitle}</Col>
|
||||
<Col span={'3'}>
|
||||
<div>
|
||||
<Icon
|
||||
onClick={this.toggleShowClass}
|
||||
className="icon-toggle"
|
||||
type={closed ? 'arrow-down' : 'arrow-up'}
|
||||
style={closed ? { top: '-2px' } : { top: '0' }}
|
||||
/>
|
||||
<Icon
|
||||
type="delete"
|
||||
size={'small'}
|
||||
className="icon-delete"
|
||||
onClick={() => {
|
||||
if (this.props.delete) {
|
||||
this.props.delete(this.props.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<div className={`array-item-group-box ${closed ? 'disable' : ''}`}>{children}</div>
|
||||
</div>
|
||||
</Loading>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ArrayItemGroup;
|
|
@ -5,6 +5,7 @@ type Props = {
|
|||
value: any;
|
||||
id: string;
|
||||
onChange: (value: any) => void;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
@ -27,7 +28,7 @@ class CPUNumber extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { value, id } = this.props;
|
||||
const { value, id, disabled } = this.props;
|
||||
let initValue = undefined;
|
||||
if (value) {
|
||||
initValue = parseFloat(value);
|
||||
|
@ -36,6 +37,7 @@ class CPUNumber extends React.Component<Props, State> {
|
|||
return (
|
||||
<Input
|
||||
id={id}
|
||||
disabled={disabled}
|
||||
addonTextAfter="Core"
|
||||
htmlType="number"
|
||||
onChange={this.onChange}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
import { Input } from '@b-design/ui';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
onChange: (value: any) => void;
|
||||
value: any;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
||||
class DiskNumber extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
onChange = (value: string) => {
|
||||
const { onChange } = this.props;
|
||||
if (onChange) {
|
||||
onChange(value + 'Gi');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, id, disabled } = this.props;
|
||||
let initValue = undefined;
|
||||
if (value) {
|
||||
initValue = parseInt(value.replace('Gi', ''));
|
||||
}
|
||||
return (
|
||||
<Input
|
||||
id={id}
|
||||
disabled={disabled}
|
||||
min="0"
|
||||
addonTextAfter="Gi"
|
||||
htmlType="number"
|
||||
onChange={this.onChange}
|
||||
value={initValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DiskNumber;
|
|
@ -18,7 +18,12 @@
|
|||
outline: none;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.12s;
|
||||
|
||||
.group-title.required::before {
|
||||
margin-right: 4px;
|
||||
color: #c80000;
|
||||
color: var(--form-error-color, #c80000);
|
||||
content: '*';
|
||||
}
|
||||
.group-title-desc {
|
||||
color: #a6a6a6;
|
||||
font-weight: normal;
|
||||
|
@ -35,11 +40,8 @@
|
|||
}
|
||||
}
|
||||
.group-box {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 20px;
|
||||
background-color: #eee;
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
transition: height 0.3s linear;
|
||||
}
|
||||
|
|
|
@ -83,14 +83,13 @@ class Group extends React.Component<Props, State> {
|
|||
disableAddon = false,
|
||||
} = this.props;
|
||||
const { closed, enable, checked } = this.state;
|
||||
|
||||
return (
|
||||
<Loading visible={loading || false} style={{ width: '100%' }}>
|
||||
<div className="group-container">
|
||||
<div className="group-title-container">
|
||||
<Row>
|
||||
<Col span={'21'}>
|
||||
{title}
|
||||
<span className={`group-title ${required && 'required'}`}>{title}</span>
|
||||
<div className="group-title-desc">{description}</div>
|
||||
</Col>
|
||||
<Col span={'3'} className="flexcenter">
|
||||
|
@ -108,7 +107,7 @@ class Group extends React.Component<Props, State> {
|
|||
type: 'confirm',
|
||||
content: (
|
||||
<Translation>
|
||||
If Swtich is turned off, The configuration will be reset. Are you sure
|
||||
If Switch is turned off, The configuration will be reset. Are you sure
|
||||
you want to do this?
|
||||
</Translation>
|
||||
),
|
||||
|
@ -132,11 +131,8 @@ class Group extends React.Component<Props, State> {
|
|||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<If condition={enable && (!hasToggleIcon || !closed)}>
|
||||
<div className="group-box">{children}</div>
|
||||
</If>
|
||||
<If condition={closed && enable}>
|
||||
<div className="group-box disable">{children}</div>
|
||||
<If condition={enable}>
|
||||
<div className={`group-box ${closed ? 'disable' : ''}`}>{children}</div>
|
||||
</If>
|
||||
</div>
|
||||
</Loading>
|
||||
|
|
|
@ -11,6 +11,7 @@ type Props = {
|
|||
additionalParameter?: UIParam;
|
||||
subParameters?: UIParam[];
|
||||
id: string;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
function setValues(target: any, value: any, key: string, keys: string[]) {
|
||||
|
@ -65,6 +66,7 @@ class HelmValues extends Component<Props> {
|
|||
render() {
|
||||
return (
|
||||
<KV
|
||||
disabled={this.props.disabled}
|
||||
onChange={this.onChange}
|
||||
value={this.renderValue()}
|
||||
additional={this.props.additional}
|
||||
|
|
|
@ -2,12 +2,13 @@ import React from 'react';
|
|||
import { Input } from '@b-design/ui';
|
||||
import './index.less';
|
||||
|
||||
import { InputProps } from '@alifd/next/types/input';
|
||||
import type { InputProps } from '@alifd/next/types/input';
|
||||
|
||||
interface Props extends InputProps {
|
||||
value: any;
|
||||
id: string;
|
||||
onChange: (value: any) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
type State = {};
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
.group-inner-container {
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
.ui-schema-container {
|
||||
display: flex;
|
||||
.next-select {
|
||||
width: 100%;
|
||||
}
|
||||
.next-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
import React from 'react';
|
||||
import type { UIParam } from '../../interface/application';
|
||||
import UISchema from '../../components/UISchema';
|
||||
import './index.less';
|
||||
|
||||
type Props = {
|
||||
_key?: string;
|
||||
title: string | React.ReactNode;
|
||||
description?: string | React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
uiSchema: UIParam[] | undefined;
|
||||
onChange?: (params: any) => void;
|
||||
value: any;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
||||
class InnerGroup extends React.Component<Props, State> {
|
||||
dom: any;
|
||||
ref: React.RefObject<UISchema>;
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.ref = React.createRef();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { uiSchema, onChange, value } = this.props;
|
||||
return (
|
||||
<div className="group-inner-container">
|
||||
<UISchema
|
||||
key={value}
|
||||
ref={this.ref}
|
||||
uiSchema={uiSchema}
|
||||
value={value}
|
||||
inline
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default InnerGroup;
|
|
@ -13,6 +13,7 @@ type Props = {
|
|||
additionalParameter?: UIParam;
|
||||
subParameters?: UIParam[];
|
||||
id: string;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -136,6 +137,7 @@ class KV extends Component<Props, State> {
|
|||
<Col span={10}>
|
||||
<Form.Item>
|
||||
<Input
|
||||
disabled={this.props.disabled}
|
||||
{...init(`envKey-${item.key}`)}
|
||||
label={'Key'}
|
||||
className="full-width"
|
||||
|
@ -146,6 +148,7 @@ class KV extends Component<Props, State> {
|
|||
<Col span={10}>
|
||||
<Form.Item>
|
||||
<Input
|
||||
disabled={this.props.disabled}
|
||||
htmlType={valueTypeNumber ? 'number' : ''}
|
||||
{...init(`envValue-${item.key}`)}
|
||||
label={'Value'}
|
||||
|
@ -163,7 +166,12 @@ class KV extends Component<Props, State> {
|
|||
);
|
||||
})}
|
||||
<div className="mb-20 flexright">
|
||||
<Button size="small" type="secondary" onClick={this.addItem.bind(this)}>
|
||||
<Button
|
||||
disabled={this.props.disabled}
|
||||
size="small"
|
||||
type="secondary"
|
||||
onClick={this.addItem.bind(this)}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,7 @@ type Props = {
|
|||
id: string;
|
||||
onChange: (value: any) => void;
|
||||
value: any;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
@ -23,7 +24,7 @@ class MemoryNumber extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { value, id } = this.props;
|
||||
const { value, id, disabled } = this.props;
|
||||
let initValue = undefined;
|
||||
if (value) {
|
||||
initValue = parseInt(value.replace('Mi', ''));
|
||||
|
@ -32,6 +33,7 @@ class MemoryNumber extends React.Component<Props, State> {
|
|||
<Input
|
||||
id={id}
|
||||
min="0"
|
||||
disabled={disabled}
|
||||
addonTextAfter="MB"
|
||||
htmlType="number"
|
||||
onChange={this.onChange}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import React from 'react';
|
||||
import { Select } from '@b-design/ui';
|
||||
import locale from '../../utils/locale';
|
||||
import i18n from 'i18next';
|
||||
|
||||
type Props = {
|
||||
onChange: (value: any) => void;
|
||||
secretKeys?: string[];
|
||||
value: any;
|
||||
id: string;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
@ -22,9 +24,17 @@ class SecretKeySelect extends React.Component<Props, State> {
|
|||
componentDidMount = async () => {};
|
||||
|
||||
render() {
|
||||
const { onChange, value, secretKeys, id } = this.props;
|
||||
const { onChange, value, secretKeys, id, disabled } = this.props;
|
||||
return (
|
||||
<Select locale={locale.Select} onChange={onChange} defaultValue={value} id={id} value={value}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
onChange={onChange}
|
||||
defaultValue={value}
|
||||
id={id}
|
||||
disabled={disabled}
|
||||
value={value}
|
||||
placeholder={i18n.t('Please select the secret key').toString()}
|
||||
>
|
||||
{secretKeys?.map((item) => {
|
||||
return (
|
||||
<Select.Option key={item} value={item}>
|
||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
import { listCloudResourceSecrets } from '../../api/observation';
|
||||
import type { Secret } from '../../interface/observation';
|
||||
import locale from '../../utils/locale';
|
||||
import i18n from 'i18next';
|
||||
|
||||
type Props = {
|
||||
onChange: (value: any) => void;
|
||||
|
@ -11,6 +12,7 @@ type Props = {
|
|||
value: any;
|
||||
id: string;
|
||||
appNamespace?: string;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -58,11 +60,18 @@ class SecretSelect extends React.Component<Props, State> {
|
|||
}
|
||||
};
|
||||
render() {
|
||||
const { value, id } = this.props;
|
||||
const { value, id, disabled } = this.props;
|
||||
const { secrets } = this.state;
|
||||
const filters = secrets?.filter((secret) => secret.metadata.labels['app.oam.dev/sync-alias']);
|
||||
return (
|
||||
<Select locale={locale.Select} onChange={this.onChange} value={value} id={id}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
onChange={this.onChange}
|
||||
value={value}
|
||||
id={id}
|
||||
disabled={disabled}
|
||||
placeholder={i18n.t('Please select the secret').toString()}
|
||||
>
|
||||
{filters?.map((secret) => {
|
||||
return (
|
||||
<Select.Option
|
||||
|
|
|
@ -9,6 +9,7 @@ type Props = {
|
|||
value: any;
|
||||
id: string;
|
||||
onChange: (value: any) => void;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type InputParams = {
|
||||
|
@ -19,6 +20,7 @@ type InputParams = {
|
|||
value?: string;
|
||||
onChange: () => {};
|
||||
delete: (key: string) => {};
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type ListParams = {
|
||||
|
@ -38,6 +40,7 @@ function InputItem(props: InputParams) {
|
|||
onChange={props.onChange}
|
||||
addonBefore={''}
|
||||
className="input"
|
||||
disabled={props.disabled}
|
||||
value={props.value}
|
||||
/>
|
||||
<div className="remove-option-container">
|
||||
|
@ -128,7 +131,7 @@ class Strings extends React.Component<Props, State> {
|
|||
render() {
|
||||
const { inputList } = this.state;
|
||||
const { init } = this.field;
|
||||
const { label } = this.props;
|
||||
const { label, disabled } = this.props;
|
||||
return (
|
||||
<div className="strings-container">
|
||||
{inputList.map((item) => (
|
||||
|
@ -142,7 +145,7 @@ class Strings extends React.Component<Props, State> {
|
|||
/>
|
||||
))}
|
||||
<div className="add-btn-container">
|
||||
<Button size="small" onClick={this.addInputItem} ghost="light">
|
||||
<Button disabled={disabled} size="small" onClick={this.addInputItem} ghost="light">
|
||||
<Translation>Add</Translation>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
padding: 10px 20px;
|
||||
background-color: #e2e2e2;
|
||||
border-radius: 24px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 4px;
|
||||
.struct-item-content {
|
||||
width: 100%;
|
||||
}
|
||||
.struct-item-content > .next-form {
|
||||
display: flex !important;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
.next-form-item {
|
||||
display: flex;
|
||||
margin-right: 12px;
|
||||
|
@ -27,7 +25,7 @@
|
|||
margin-right: 0;
|
||||
}
|
||||
.next-form-item-label {
|
||||
flex: 0 0 55px;
|
||||
flex: 0 0 85px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Form, Icon, Field, Button } from '@b-design/ui';
|
|||
import { If } from 'tsx-control-statements/components';
|
||||
import type { UIParam, GroupOption } from '../../interface/application';
|
||||
import UISchema from '../../components/UISchema';
|
||||
import ArrayItemGroup from '../ArrayItemGroup';
|
||||
import type { Rule } from '@alifd/field';
|
||||
import './index.less';
|
||||
|
||||
|
@ -14,6 +15,8 @@ type Props = {
|
|||
registerForm: (form: Field) => void;
|
||||
id: string;
|
||||
value: any;
|
||||
label: string;
|
||||
mode: 'new' | 'edit';
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -25,8 +28,11 @@ type StructItemProps = {
|
|||
param?: UIParam[];
|
||||
id: string;
|
||||
init: any;
|
||||
labelTitle: string | React.ReactElement;
|
||||
delete: (id: string) => void;
|
||||
mode: 'new' | 'edit';
|
||||
};
|
||||
|
||||
class StructItem extends React.Component<StructItemProps> {
|
||||
uiRef: React.RefObject<UISchema>;
|
||||
constructor(props: StructItemProps) {
|
||||
|
@ -39,8 +45,23 @@ class StructItem extends React.Component<StructItemProps> {
|
|||
validator = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||
this.uiRef.current?.validate(callback);
|
||||
};
|
||||
getParamCount = (params: UIParam[] | undefined) => {
|
||||
let count = 0;
|
||||
if (!params && !Array.isArray(params)) {
|
||||
return count;
|
||||
}
|
||||
params.map((p) => {
|
||||
if (!p.disable && p.uiType != 'Ignore' && p.uiType != 'InnerGroup') {
|
||||
count += 1;
|
||||
}
|
||||
if (!p.disable && p.subParameters) {
|
||||
count += this.getParamCount(p.subParameters);
|
||||
}
|
||||
});
|
||||
return count;
|
||||
};
|
||||
render() {
|
||||
const { option, param, id, init } = this.props;
|
||||
const { option, param, id, init, labelTitle } = this.props;
|
||||
let uiSchemas = param;
|
||||
if (option && option.length > 0) {
|
||||
const paramMap =
|
||||
|
@ -51,34 +72,65 @@ class StructItem extends React.Component<StructItemProps> {
|
|||
}, {});
|
||||
uiSchemas = option.map((key: string) => paramMap[key]);
|
||||
}
|
||||
|
||||
const paramCount = this.getParamCount(uiSchemas);
|
||||
const itemCount = uiSchemas?.filter((p) => !p.disable).length || 1;
|
||||
return (
|
||||
<div className="struct-item-container">
|
||||
<div className="struct-item-content">
|
||||
<UISchema
|
||||
{...init(`struct${id}`, {
|
||||
rules: [
|
||||
{
|
||||
validator: this.validator,
|
||||
message: 'please check config item',
|
||||
},
|
||||
],
|
||||
})}
|
||||
uiSchema={uiSchemas}
|
||||
inline
|
||||
ref={this.uiRef}
|
||||
/>
|
||||
</div>
|
||||
<div className="remove-option-container">
|
||||
<Icon
|
||||
type="ashbin"
|
||||
onClick={() => {
|
||||
if (this.props.delete) {
|
||||
this.props.delete(this.props.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<If condition={paramCount > 3}>
|
||||
<div className="struct-item-content">
|
||||
<ArrayItemGroup
|
||||
id={id}
|
||||
labelTitle={labelTitle}
|
||||
delete={(structId: string) => {
|
||||
this.props.delete(structId);
|
||||
}}
|
||||
>
|
||||
<UISchema
|
||||
{...init(`struct${id}`, {
|
||||
rules: [
|
||||
{
|
||||
validator: this.validator,
|
||||
message: 'please check config item',
|
||||
},
|
||||
],
|
||||
})}
|
||||
uiSchema={uiSchemas}
|
||||
inline
|
||||
ref={this.uiRef}
|
||||
mode={this.props.mode}
|
||||
/>
|
||||
</ArrayItemGroup>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={paramCount <= 3}>
|
||||
<div className="struct-item-content">
|
||||
<UISchema
|
||||
{...init(`struct${id}`, {
|
||||
rules: [
|
||||
{
|
||||
validator: this.validator,
|
||||
message: 'please check config item',
|
||||
},
|
||||
],
|
||||
})}
|
||||
uiSchema={uiSchemas}
|
||||
maxColSpan={24 / itemCount}
|
||||
inline
|
||||
ref={this.uiRef}
|
||||
mode={this.props.mode}
|
||||
/>
|
||||
</div>
|
||||
<div className="remove-option-container">
|
||||
<Icon
|
||||
type="ashbin"
|
||||
onClick={() => {
|
||||
if (this.props.delete) {
|
||||
this.props.delete(this.props.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -107,9 +159,13 @@ class Structs extends React.Component<Props, State> {
|
|||
const { value, parameterGroupOption } = this.props;
|
||||
if (value) {
|
||||
const keyMap = new Map();
|
||||
let firstOption: GroupOption | undefined = undefined;
|
||||
if (parameterGroupOption) {
|
||||
parameterGroupOption.map((item) => {
|
||||
if (item && item.keys) {
|
||||
if (!firstOption) {
|
||||
firstOption = item;
|
||||
}
|
||||
keyMap.set(item.keys.sort().join(), item);
|
||||
}
|
||||
});
|
||||
|
@ -124,7 +180,7 @@ class Structs extends React.Component<Props, State> {
|
|||
const option = keyMap.get(valueKeys.sort().join());
|
||||
structList.push({
|
||||
key,
|
||||
option: option?.keys,
|
||||
option: option?.keys || firstOption?.keys,
|
||||
value: value,
|
||||
});
|
||||
this.field.setValue('struct' + key, item);
|
||||
|
@ -179,22 +235,36 @@ class Structs extends React.Component<Props, State> {
|
|||
|
||||
render() {
|
||||
const { structList } = this.state;
|
||||
const { param, parameterGroupOption = [] } = this.props;
|
||||
const { param, parameterGroupOption = [], label } = this.props;
|
||||
const { init } = this.field;
|
||||
return (
|
||||
<div className="struct-plan-container">
|
||||
<div className="struct-plan-group">
|
||||
<Form field={this.field}>
|
||||
{structList.map((struct: any) => (
|
||||
<StructItem
|
||||
delete={this.removeStructPlanItem}
|
||||
id={struct.key}
|
||||
key={struct.key}
|
||||
init={init}
|
||||
option={struct.option}
|
||||
param={param}
|
||||
/>
|
||||
))}
|
||||
{structList.map((struct: any) => {
|
||||
const fieldObj: any = this.field.getValues();
|
||||
const name = fieldObj[`struct${struct.key}`]?.name || '';
|
||||
let labelTitle: string | React.ReactElement = label;
|
||||
if (name) {
|
||||
labelTitle = (
|
||||
<span>
|
||||
{label}: <span style={{ marginLeft: '8px' }}>{name}</span>{' '}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<StructItem
|
||||
delete={this.removeStructPlanItem}
|
||||
id={struct.key}
|
||||
key={struct.key}
|
||||
init={init}
|
||||
option={struct.option}
|
||||
param={param}
|
||||
labelTitle={labelTitle}
|
||||
mode={this.props.mode}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Form>
|
||||
</div>
|
||||
<div className="struct-plan-option">
|
||||
|
|
|
@ -8,11 +8,12 @@ export interface Addon {
|
|||
detail?: string;
|
||||
tags?: string[];
|
||||
createTime?: string;
|
||||
deployTo?: { controlPlane: boolean; runtimeCluster: boolean };
|
||||
deployTo?: { controlPlane: boolean; runtimeCluster: boolean; runtime_cluster: boolean };
|
||||
dependencies?: { name: string }[];
|
||||
definitions?: Definition[];
|
||||
uiSchema?: UIParam[];
|
||||
registryName?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface AddonStatus {
|
||||
|
|
|
@ -57,6 +57,9 @@ export interface UIParam {
|
|||
label: string;
|
||||
sort: number;
|
||||
uiType: string;
|
||||
style?: {
|
||||
colSpan: number;
|
||||
};
|
||||
disable?: boolean;
|
||||
subParameterGroupOption?: GroupOption[];
|
||||
additional?: boolean;
|
||||
|
@ -79,6 +82,7 @@ export interface UIParamValidate {
|
|||
pattern?: string;
|
||||
defaultValue?: any;
|
||||
options?: { label: string; value: string }[];
|
||||
immutable?: boolean;
|
||||
}
|
||||
|
||||
export interface ImageInfo {
|
||||
|
@ -209,7 +213,7 @@ export interface Revisions {
|
|||
export interface ApplicationStatistics {
|
||||
envCount: number;
|
||||
targetCount: number;
|
||||
revisonCount: number;
|
||||
revisionCount: number;
|
||||
workflowCount: number;
|
||||
}
|
||||
|
||||
|
@ -260,3 +264,10 @@ export interface Trigger {
|
|||
createTime?: string;
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
export interface WorkflowStep {
|
||||
name: string;
|
||||
alias: string;
|
||||
description?: string;
|
||||
type: string;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
.deployConfig {
|
||||
min-height: auto;
|
||||
.next-radio-group {
|
||||
width: 100%;
|
||||
.next-radio-wrapper {
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
padding: 16px 32px;
|
||||
border: 1px solid #c0c6cc !important;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
.env {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
.next-radio-wrapper:hover {
|
||||
border: 1px solid #0064c8 !important;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import React, { Component } from 'react';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { Dialog, Radio } from '@b-design/ui';
|
||||
import type { Workflow } from '../../../../interface/application';
|
||||
import Translation from '../../../../components/Translation';
|
||||
import locale from '../../../../utils/locale';
|
||||
import './index.less';
|
||||
|
||||
const { Group: RadioGroup } = Radio;
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
onOK: (workflowName?: string, force?: boolean) => void;
|
||||
workflows?: Workflow[];
|
||||
}
|
||||
|
||||
interface State {
|
||||
loading: boolean;
|
||||
workflowName: string;
|
||||
}
|
||||
|
||||
class DeployConfigDialog extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
workflowName: '',
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { workflows } = this.props;
|
||||
workflows?.map((w) => {
|
||||
if (w.default) {
|
||||
this.setState({ workflowName: w.name });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit = () => {
|
||||
if (this.state.workflowName) {
|
||||
this.props.onOK(this.state.workflowName);
|
||||
this.props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
onChange = (name: any) => {
|
||||
this.setState({ workflowName: name });
|
||||
};
|
||||
render() {
|
||||
const { workflows, onClose } = this.props;
|
||||
const { workflowName } = this.state;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Dialog
|
||||
visible={true}
|
||||
locale={locale.Dialog}
|
||||
className={'commonDialog deployConfig'}
|
||||
style={{ width: '600px' }}
|
||||
isFullScreen={true}
|
||||
footerActions={['cancel', 'ok']}
|
||||
onClose={onClose}
|
||||
onCancel={onClose}
|
||||
onOk={this.onSubmit}
|
||||
title={<Translation>Select Workflow</Translation>}
|
||||
>
|
||||
<RadioGroup value={workflowName} onChange={this.onChange}>
|
||||
{workflows?.map((workflow) => {
|
||||
return (
|
||||
<Radio
|
||||
id={workflow.name}
|
||||
value={workflow.name}
|
||||
onClick={() => {
|
||||
this.onChange(workflow.name);
|
||||
}}
|
||||
>
|
||||
{workflow.alias ? workflow.alias : workflow.name}
|
||||
<span className="env">Env: {workflow.envName}</span>
|
||||
</Radio>
|
||||
);
|
||||
})}
|
||||
</RadioGroup>
|
||||
</Dialog>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation()(DeployConfigDialog);
|
|
@ -31,6 +31,7 @@
|
|||
.item {
|
||||
color: #1b58f4;
|
||||
}
|
||||
|
||||
border-color: #1b58f4 !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
import {
|
||||
Grid,
|
||||
Card,
|
||||
Breadcrumb,
|
||||
Button,
|
||||
Message,
|
||||
Icon,
|
||||
Dropdown,
|
||||
Menu,
|
||||
Dialog,
|
||||
} from '@b-design/ui';
|
||||
import { Grid, Card, Breadcrumb, Button, Message, Dialog } from '@b-design/ui';
|
||||
import { connect } from 'dva';
|
||||
import React, { Component } from 'react';
|
||||
import Translation from '../../../../components/Translation';
|
||||
|
@ -32,6 +22,7 @@ import WorkflowSilder from '../WorkflowSilder';
|
|||
import { If } from 'tsx-control-statements/components';
|
||||
import Empty from '../../../../components/Empty';
|
||||
import locale from '../../../../utils/locale';
|
||||
import DeployConfig from '../DeployConfig';
|
||||
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
|
@ -47,6 +38,7 @@ interface State {
|
|||
loading: boolean;
|
||||
statistics?: ApplicationStatistics;
|
||||
records?: WorkflowBase[];
|
||||
showDeployConfig: boolean;
|
||||
}
|
||||
|
||||
@connect((store: any) => {
|
||||
|
@ -59,10 +51,15 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
super(props);
|
||||
this.state = {
|
||||
loading: true,
|
||||
showDeployConfig: false,
|
||||
};
|
||||
this.sync = true;
|
||||
}
|
||||
|
||||
onDeployConfig = () => {
|
||||
this.setState({ showDeployConfig: true });
|
||||
};
|
||||
|
||||
onDeploy = (workflowName?: string, force?: boolean) => {
|
||||
const { applicationDetail } = this.props;
|
||||
if (applicationDetail) {
|
||||
|
@ -140,7 +137,7 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
|
||||
render() {
|
||||
const { applicationDetail, currentPath, workflows, appName } = this.props;
|
||||
const { statistics, records } = this.state;
|
||||
const { statistics, records, showDeployConfig } = this.state;
|
||||
const activeKey = currentPath.substring(currentPath.lastIndexOf('/') + 1);
|
||||
const item = <Translation>{`app-${activeKey}`}</Translation>;
|
||||
return (
|
||||
|
@ -164,31 +161,13 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
type="notice"
|
||||
title="Application configuration changes take effect only after deploy."
|
||||
/>
|
||||
<Button style={{ marginLeft: '16px' }} type="primary" onClick={() => this.onDeploy()}>
|
||||
<Button
|
||||
style={{ marginLeft: '16px' }}
|
||||
type="primary"
|
||||
onClick={() => this.onDeployConfig()}
|
||||
>
|
||||
<Translation>Deploy</Translation>
|
||||
</Button>
|
||||
<Dropdown
|
||||
trigger={
|
||||
<Button type="primary">
|
||||
<Icon type="ellipsis-vertical" />
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Menu>
|
||||
{workflows?.map((workflow) => {
|
||||
return (
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
this.onDeploy(workflow.name);
|
||||
}}
|
||||
key={workflow.name}
|
||||
>
|
||||
<Translation>Execute Workflow</Translation> {workflow.alias || workflow.name}
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row wrap={true}>
|
||||
|
@ -199,10 +178,10 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
<NumItem number={statistics?.envCount} title={'Env Count'} />
|
||||
</Col>
|
||||
<Col span={6} style={{ padding: '22px 0' }}>
|
||||
<NumItem number={statistics?.targetCount} title={'target Count'} />
|
||||
<NumItem number={statistics?.targetCount} title={'Target Count'} />
|
||||
</Col>
|
||||
<Col span={6} style={{ padding: '22px 0' }}>
|
||||
<NumItem number={statistics?.revisonCount} title={'Revision Count'} />
|
||||
<NumItem number={statistics?.revisionCount} title={'Revision Count'} />
|
||||
</Col>
|
||||
<Col span={6} style={{ padding: '22px 0' }}>
|
||||
<NumItem number={statistics?.workflowCount} title={'Workflow Count'} />
|
||||
|
@ -224,6 +203,15 @@ class ApplicationHeader extends Component<Props, State> {
|
|||
</If>
|
||||
</Col>
|
||||
</Row>
|
||||
<If condition={showDeployConfig}>
|
||||
<DeployConfig
|
||||
onClose={() => {
|
||||
this.setState({ showDeployConfig: false });
|
||||
}}
|
||||
onOK={this.onDeploy}
|
||||
workflows={workflows}
|
||||
/>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -90,3 +90,9 @@ a:hover {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Icon } from '@b-design/ui';
|
|||
import Translation from '../../components/Translation';
|
||||
import { getLeftSider } from './menu';
|
||||
import './index.less';
|
||||
import { version } from '../../../package.json';
|
||||
|
||||
const LeftMenu = (props: any) => {
|
||||
const pathname = _.get(props, 'props.history.location.pathname');
|
||||
|
@ -45,6 +46,9 @@ const LeftMenu = (props: any) => {
|
|||
<div style={{ position: 'relative', height: '100%' }}>
|
||||
<div className="slide-wraper">
|
||||
<ul className="ul-wraper">{childrenSider}</ul>
|
||||
<div className="bottom">
|
||||
<div className="nav-container">Version {version}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -48,7 +48,7 @@ class TopBar extends Component<Props, State> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadSystemInfo();
|
||||
//this.loadSystemInfo();
|
||||
}
|
||||
|
||||
loadSystemInfo = () => {
|
||||
|
@ -183,13 +183,13 @@ class TopBar extends Component<Props, State> {
|
|||
<Icon size={14} type="help1" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="vela-item">
|
||||
{/* <div className="vela-item">
|
||||
<Icon
|
||||
onClick={this.showUserExperienceImprovementPlan}
|
||||
size={14}
|
||||
type="exclamation-circle"
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</Row>
|
||||
<Dialog
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
.layout-navigation {
|
||||
width: 240px;
|
||||
min-width: 60px;
|
||||
min-height: 100vh;
|
||||
min-height: calc(100vh - 48px);
|
||||
padding-top: 22px;
|
||||
background-color: #252525;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@
|
|||
.layout-content {
|
||||
flex: 1;
|
||||
width: calc(100% - 240px);
|
||||
min-height: 100vh;
|
||||
min-height: calc(100vh - 48px);
|
||||
padding: 15px;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
|
|
@ -180,5 +180,7 @@
|
|||
"Needs review before continuing": "需求审核后才能继续执行",
|
||||
"Please select the action you want to perform": "请选择您期望的动作执行?",
|
||||
"Continue": "继续",
|
||||
"Termination": "终止"
|
||||
}
|
||||
"Termination": "终止",
|
||||
"Container Image": "容器镜像",
|
||||
"Which image would you like to use for your service": "请在部署之前完成容器镜像准备并上传到镜像仓库中。"
|
||||
}
|
|
@ -105,29 +105,34 @@ export default {
|
|||
workflow.default = option.default;
|
||||
const { nodes, edges } = data;
|
||||
const nodeIndex = {};
|
||||
let index = 0;
|
||||
const steps = Object.keys(nodes).map((key) => {
|
||||
Object.keys(nodes).map((key) => {
|
||||
if (nodes[key]) {
|
||||
nodeIndex[nodes[key].id] = index;
|
||||
index++;
|
||||
const nodeConfig = nodes[key].consumerData;
|
||||
let nodeConfig = Object.assign({}, nodes[key].consumerData);
|
||||
if (nodeConfig && nodeConfig.properties && typeof nodeConfig.properties != 'string') {
|
||||
return Object.assign(nodeConfig, { properties: JSON.stringify(nodeConfig.properties) });
|
||||
nodeConfig = Object.assign(nodeConfig, {
|
||||
properties: JSON.stringify(nodeConfig.properties),
|
||||
});
|
||||
}
|
||||
return nodeConfig;
|
||||
nodeIndex[nodes[key].id] = nodeConfig;
|
||||
}
|
||||
});
|
||||
let next = edges.prev;
|
||||
let steps = [];
|
||||
let srcMap = {};
|
||||
Object.keys(edges).map((key) => {
|
||||
const edge = edges[key];
|
||||
if (nodeIndex[edge.src] > nodeIndex[edge.dest]) {
|
||||
const i = steps[nodeIndex[edge.src]];
|
||||
const oldIndex = nodeIndex[edge.src];
|
||||
steps[nodeIndex[edge.src]] = steps[nodeIndex[edge.dest]];
|
||||
steps[nodeIndex[edge.dest]] = i;
|
||||
nodeIndex[edge.src] = nodeIndex[edge.dest];
|
||||
nodeIndex[edge.dest] = oldIndex;
|
||||
if (edges[key].src == 'prev') {
|
||||
next = edges[key];
|
||||
}
|
||||
srcMap[edges[key].src] = edges[key];
|
||||
});
|
||||
while (next != undefined) {
|
||||
if (next.dest && next.dest != 'next') {
|
||||
steps.push(nodeIndex[next.dest]);
|
||||
next = srcMap[next.dest];
|
||||
} else {
|
||||
next = undefined;
|
||||
}
|
||||
}
|
||||
workflow.steps = steps;
|
||||
yield call(createWorkFlow, workflow);
|
||||
if (action.callback) {
|
||||
|
@ -141,7 +146,7 @@ function transData(workflowList = [], appName) {
|
|||
const newWorkflows = _.cloneDeep(workflowList);
|
||||
if (newWorkflows && newWorkflows.length != 0) {
|
||||
newWorkflows.forEach((workflow) => {
|
||||
convertWorkflowStep(workflow, appName, 32);
|
||||
convertWorkflowStep(workflow, appName, 0);
|
||||
});
|
||||
return newWorkflows;
|
||||
} else {
|
||||
|
@ -149,18 +154,39 @@ function transData(workflowList = [], appName) {
|
|||
}
|
||||
}
|
||||
|
||||
export function convertWorkflowStep(workflow, appName, initPosition = 32) {
|
||||
const nodeWidth = 140;
|
||||
const nodeHeight = 40;
|
||||
const nodeInterval = 80;
|
||||
export function convertWorkflowStep(workflow, appName, initPosition = 32, edit = false) {
|
||||
const nodeWidth = 200;
|
||||
const nodeHeight = 80;
|
||||
const nodeInterval = 120;
|
||||
const nodes = {};
|
||||
const edges = {};
|
||||
let position = initPosition;
|
||||
if (edit) {
|
||||
nodes.prev = {
|
||||
id: 'prev',
|
||||
typeId: 'prev',
|
||||
diagramMakerData: {
|
||||
position: {
|
||||
x: position,
|
||||
y: 130,
|
||||
},
|
||||
size: {
|
||||
width: nodeWidth,
|
||||
height: nodeHeight,
|
||||
},
|
||||
selected: false,
|
||||
},
|
||||
};
|
||||
position += nodeWidth + nodeInterval;
|
||||
}
|
||||
if (workflow.steps) {
|
||||
workflow.steps.forEach((item, index, array) => {
|
||||
edges[item.name] = {};
|
||||
edges[item.name].dest =
|
||||
workflow.steps && workflow.steps[index + 1] && workflow.steps[index + 1].name;
|
||||
if (workflow.steps && workflow.steps[index + 1]) {
|
||||
edges[item.name].dest = workflow.steps[index + 1].name;
|
||||
} else if (edit) {
|
||||
edges[item.name].dest = 'next';
|
||||
}
|
||||
edges[item.name].diagramMakerData = {
|
||||
selected: false,
|
||||
};
|
||||
|
@ -169,7 +195,7 @@ export function convertWorkflowStep(workflow, appName, initPosition = 32) {
|
|||
|
||||
nodes[item.name] = {};
|
||||
nodes[item.name].id = item.name;
|
||||
nodes[item.name].typeId = item.type;
|
||||
nodes[item.name].typeId = 'common';
|
||||
nodes[item.name].consumerData = {
|
||||
alias: item.alias || '',
|
||||
dependsOn: null,
|
||||
|
@ -191,8 +217,32 @@ export function convertWorkflowStep(workflow, appName, initPosition = 32) {
|
|||
};
|
||||
position += nodeWidth + nodeInterval;
|
||||
});
|
||||
edges.prev = {
|
||||
dest: workflow.steps[0].name,
|
||||
src: 'prev',
|
||||
id: 'prev',
|
||||
diagramMakerData: {
|
||||
selected: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (edit) {
|
||||
nodes.next = {
|
||||
id: 'next',
|
||||
typeId: 'next',
|
||||
diagramMakerData: {
|
||||
position: {
|
||||
x: position,
|
||||
y: 130,
|
||||
},
|
||||
size: {
|
||||
width: nodeWidth,
|
||||
height: nodeHeight,
|
||||
},
|
||||
selected: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
workflow.appName = appName;
|
||||
workflow.option = {
|
||||
edit: false,
|
||||
|
|
|
@ -41,6 +41,19 @@ class CardContent extends React.Component<Props, State> {
|
|||
render() {
|
||||
const { Row, Col } = Grid;
|
||||
const { addonLists, clickAddon, enabledAddons } = this.props;
|
||||
|
||||
const getTagColor = (tag: string) => {
|
||||
switch (tag) {
|
||||
case 'alpha':
|
||||
return 'red';
|
||||
case 'beta':
|
||||
return 'red';
|
||||
case 'GA':
|
||||
return 'green';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<If condition={addonLists}>
|
||||
|
@ -82,7 +95,15 @@ class CardContent extends React.Component<Props, State> {
|
|||
<If condition={tags}>
|
||||
<Row className="content-main-btn">
|
||||
{tags?.map((tag: string) => {
|
||||
return <Tag key={tag}>{tag}</Tag>;
|
||||
return (
|
||||
<Tag
|
||||
style={{ marginRight: '8px' }}
|
||||
color={getTagColor(tag)}
|
||||
key={tag}
|
||||
>
|
||||
{tag}
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</If>
|
||||
|
|
|
@ -38,6 +38,7 @@ type State = {
|
|||
args?: any;
|
||||
addonsStatus?: ApplicationStatus;
|
||||
showStatusVisible: boolean;
|
||||
mode: 'new' | 'edit';
|
||||
};
|
||||
|
||||
class AddonDetailDialog extends React.Component<Props, State> {
|
||||
|
@ -57,6 +58,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
statusLoading: true,
|
||||
upgradeLoading: false,
|
||||
showStatusVisible: false,
|
||||
mode: 'new',
|
||||
};
|
||||
this.form = new Field(this);
|
||||
this.uiSchemaRef = React.createRef();
|
||||
|
@ -98,6 +100,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
}
|
||||
if (res.args) {
|
||||
this.form.setValue('properties', res.args);
|
||||
this.setState({ mode: 'edit' });
|
||||
}
|
||||
this.setState({
|
||||
status: res.phase,
|
||||
|
@ -292,6 +295,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
})}
|
||||
ref={this.uiSchemaRef}
|
||||
uiSchema={addonDetailInfo.uiSchema}
|
||||
mode={this.state.mode}
|
||||
/>
|
||||
</Form>
|
||||
</Group>
|
||||
|
|
|
@ -157,6 +157,7 @@ class EditProperties extends React.Component<Props, State> {
|
|||
})}
|
||||
uiSchema={definitionDetail && definitionDetail.uiSchema}
|
||||
ref={this.uiSchemaRef}
|
||||
mode="edit"
|
||||
/>
|
||||
</FormItem>
|
||||
</If>
|
||||
|
|
|
@ -105,7 +105,7 @@ class TraitDialog extends React.Component<Props, State> {
|
|||
transTraitDefinitions() {
|
||||
const { traitDefinitions } = this.props;
|
||||
return (traitDefinitions || []).map((item: { name: string }) => ({
|
||||
lable: item.name,
|
||||
label: item.name,
|
||||
value: item.name,
|
||||
}));
|
||||
}
|
||||
|
@ -249,6 +249,7 @@ class TraitDialog extends React.Component<Props, State> {
|
|||
})}
|
||||
uiSchema={definitionDetail && definitionDetail.uiSchema}
|
||||
ref={this.uiSchemaRef}
|
||||
mode={this.props.isEditTrait ? 'edit' : 'new'}
|
||||
/>
|
||||
</FormItem>
|
||||
</If>
|
||||
|
|
|
@ -150,7 +150,17 @@ class PodDetail extends React.Component<Props, State> {
|
|||
const percent = Number(useMemory).valueOf() / Number(requestMemory).valueOf();
|
||||
return <Progress size="small" percent={percent * 100} />;
|
||||
}
|
||||
return <span>{record.resources?.usage?.memory}</span>;
|
||||
if (record.resources?.usage?.memory) {
|
||||
return <span>{record.resources?.usage?.memory}</span>;
|
||||
}
|
||||
if (record.resources?.requests?.memory) {
|
||||
return (
|
||||
<span>
|
||||
<Translation>Request</Translation>: {record.resources?.requests?.memory}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return <span />;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -164,7 +174,17 @@ class PodDetail extends React.Component<Props, State> {
|
|||
const percent = Number(usecpu).valueOf() / Number(requestcpu).valueOf();
|
||||
return <Progress size="small" percent={percent * 100} />;
|
||||
}
|
||||
return <span>{record.resources?.usage?.cpu}</span>;
|
||||
if (record.resources?.usage?.cpu) {
|
||||
return <span>{record.resources?.usage?.cpu}</span>;
|
||||
}
|
||||
if (record.resources?.requests?.cpu) {
|
||||
return (
|
||||
<span>
|
||||
<Translation>Request</Translation>: {record.resources?.requests?.cpu}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return <span />;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -487,7 +487,7 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
}
|
||||
if ((protocol === 'https' && port == 443) || (protocol === 'http' && port === 80)) {
|
||||
port = '';
|
||||
}else{
|
||||
} else {
|
||||
port = ':' + port;
|
||||
}
|
||||
return protocol + '://' + host + port + path;
|
||||
|
|
|
@ -44,6 +44,10 @@ type State = {
|
|||
};
|
||||
|
||||
type Callback = (envName: string) => void;
|
||||
type SelectGroupType = {
|
||||
label: string;
|
||||
children: { label: string; value: string }[];
|
||||
}[];
|
||||
|
||||
@connect((store: any) => {
|
||||
return { ...store.clusters };
|
||||
|
@ -158,10 +162,44 @@ class AppDialog extends React.Component<Props, State> {
|
|||
|
||||
transComponentDefinitions() {
|
||||
const { componentDefinitions } = this.props;
|
||||
return (componentDefinitions || []).map((item: { name: string }) => ({
|
||||
lable: item.name,
|
||||
value: item.name,
|
||||
}));
|
||||
const defaultCoreDataSource = ['k8s-objects', 'task', 'webservice', 'worker'];
|
||||
const cloud: SelectGroupType = [
|
||||
{
|
||||
label: 'Cloud',
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
const core: SelectGroupType = [
|
||||
{
|
||||
label: 'Core',
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
const custom: SelectGroupType = [
|
||||
{
|
||||
label: 'Custom',
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
(componentDefinitions || []).map((item: { name: string; workloadType: string }) => {
|
||||
if (item.workloadType === 'configurations.terraform.core.oam.dev') {
|
||||
cloud[0].children.push({
|
||||
label: item.name,
|
||||
value: item.name,
|
||||
});
|
||||
} else if (defaultCoreDataSource.includes(item.name)) {
|
||||
core[0].children.push({
|
||||
label: item.name,
|
||||
value: item.name,
|
||||
});
|
||||
} else {
|
||||
custom[0].children.push({
|
||||
label: item.name,
|
||||
value: item.name,
|
||||
});
|
||||
}
|
||||
});
|
||||
return [...core, ...custom, ...cloud];
|
||||
}
|
||||
|
||||
onDetailsComponeDefinition = (value: string) => {
|
||||
|
@ -334,6 +372,7 @@ class AppDialog extends React.Component<Props, State> {
|
|||
>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
showSearch
|
||||
className="select"
|
||||
{...init(`componentType`, {
|
||||
initValue: 'webservice',
|
||||
|
@ -399,6 +438,7 @@ class AppDialog extends React.Component<Props, State> {
|
|||
})}
|
||||
uiSchema={definitionDetail && definitionDetail.uiSchema}
|
||||
ref={this.uiSchemaRef}
|
||||
mode="new"
|
||||
/>
|
||||
</FormItem>
|
||||
</If>
|
||||
|
|
|
@ -2,31 +2,6 @@ import type { DiagramMakerNodes, DiagramMakerEdges } from 'diagram-maker';
|
|||
|
||||
export const WORFLOW_NODE_WIDTH = 120;
|
||||
|
||||
export const WORKFLOW_COMMON_PANNEL = {
|
||||
p1: {
|
||||
id: 'p1',
|
||||
position: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
},
|
||||
size: {
|
||||
width: 230,
|
||||
height: 280,
|
||||
},
|
||||
},
|
||||
p2: {
|
||||
id: 'p2',
|
||||
position: {
|
||||
x: 10,
|
||||
y: 10,
|
||||
},
|
||||
size: {
|
||||
width: 420,
|
||||
height: 40,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export type WorkFlowOption = {
|
||||
default: boolean;
|
||||
edit: boolean;
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import React, { Component } from 'react';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import { Button } from '@b-design/ui';
|
||||
import { connect } from 'dva';
|
||||
import WrokflowComponent from './workflow-component';
|
||||
import WorkflowComponent from './workflow-component';
|
||||
import type { WorkFlowData } from './entity';
|
||||
import { getWorkFlowDefinitions } from '../../api/workflows';
|
||||
|
||||
import './index.less';
|
||||
import Translation from '../../components/Translation';
|
||||
|
||||
type Props = {
|
||||
workflowList: WorkFlowData[];
|
||||
|
@ -72,19 +70,19 @@ class Workflow extends Component<Props, State> {
|
|||
return (
|
||||
<div style={{ height: '100%' }} className="workflow-wraper">
|
||||
<If condition={workflowList.length === 0}>
|
||||
<div className="empty-container">
|
||||
{/* <div className="empty-container">
|
||||
<div className="empty-word">
|
||||
<Translation>Please create workflow</Translation>
|
||||
</div>
|
||||
<Button type="primary" onClick={this.addWrokflow}>
|
||||
<Translation>New Workflow</Translation>
|
||||
</Button>
|
||||
</div>
|
||||
</div> */}
|
||||
</If>
|
||||
<If condition={workflowList.length > 0}>
|
||||
<React.Fragment>
|
||||
{workflowList.map((workflow: WorkFlowData) => (
|
||||
<WrokflowComponent
|
||||
<WorkflowComponent
|
||||
key={workflow.name + params.appName + workflow.steps}
|
||||
appName={params.appName}
|
||||
data={workflow}
|
||||
|
|
|
@ -62,12 +62,11 @@ export type NodeItem = {
|
|||
typeId: string;
|
||||
};
|
||||
|
||||
class WorkFlowComponent extends Component<Props, State> {
|
||||
class WorkflowComponent extends Component<Props, State> {
|
||||
field;
|
||||
workflowItem: any;
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
errorFocus: false,
|
||||
loading: false,
|
||||
|
@ -115,33 +114,52 @@ class WorkFlowComponent extends Component<Props, State> {
|
|||
}
|
||||
const { data } = this.props;
|
||||
const workflowData = this.workflowItem.getValues();
|
||||
const { nodes } = workflowData;
|
||||
if (nodes && Object.keys(nodes).length === 0) {
|
||||
Message.error('plase add workflow item');
|
||||
const { nodes, edges } = workflowData;
|
||||
if (edges && !edges.prev) {
|
||||
let next = undefined;
|
||||
Object.keys(edges).map((key) => {
|
||||
if (edges[key].src == 'prev') {
|
||||
next = edges[key];
|
||||
}
|
||||
});
|
||||
if (!next) {
|
||||
Message.warning('Please specify the first step');
|
||||
return;
|
||||
}
|
||||
}
|
||||
const reallyNodes: any = {};
|
||||
Object.keys(nodes).map((nodeKey) => {
|
||||
if (nodes[nodeKey].typeId !== 'next' && nodes[nodeKey].typeId !== 'prev') {
|
||||
reallyNodes[nodeKey] = nodes[nodeKey];
|
||||
}
|
||||
});
|
||||
|
||||
if (reallyNodes && Object.keys(reallyNodes).length === 0) {
|
||||
Message.warning('Please add at least one workflow step');
|
||||
this.setState({
|
||||
errorFocus: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeArr: NodeItem[] = Object.values(nodes);
|
||||
const find = nodeArr.find((item) => !item.consumerData);
|
||||
|
||||
const nodeArr: NodeItem[] = Object.values(reallyNodes);
|
||||
const find = nodeArr.find((item) => !item.consumerData || item.consumerData.name == '');
|
||||
if (find) {
|
||||
return Message.error('please enter workflow step name and type');
|
||||
return Message.warning('Please set the workflow step name and type');
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
const { name, alias, description } = values;
|
||||
data.appName = data.appName || this.props.appName;
|
||||
data.name = name;
|
||||
data.alias = alias;
|
||||
data.description = description;
|
||||
data.data = workflowData;
|
||||
data.data = Object.assign(workflowData, { nodes: reallyNodes });
|
||||
this.props.dispatch({
|
||||
type: 'workflow/saveWorkflow',
|
||||
payload: data,
|
||||
callback: () => {
|
||||
Message.success('save workflow success');
|
||||
Message.success('Save the workflow success');
|
||||
this.props.getWorkflow();
|
||||
this.setState({ loading: false });
|
||||
},
|
||||
|
@ -170,14 +188,14 @@ class WorkFlowComponent extends Component<Props, State> {
|
|||
<Translation>{option.default ? 'Cancel Default' : 'Set As Default'}</Translation>
|
||||
</Menu.Item>
|
||||
</If>
|
||||
<Menu.Item onClick={() => this.deleteWorkflow(data.name)}>
|
||||
{/* <Menu.Item onClick={() => this.deleteWorkflow(data.name)}>
|
||||
<Translation>Remove</Translation>
|
||||
</Menu.Item>
|
||||
</Menu.Item> */}
|
||||
</Menu>
|
||||
);
|
||||
const { init } = this.field;
|
||||
const newData = _.cloneDeep(data);
|
||||
convertWorkflowStep(newData, data.appName, option.edit ? 250 : 32);
|
||||
convertWorkflowStep(newData, data.appName, 32, option.edit);
|
||||
const workflowData = newData.data || { nodes: {}, edges: {} };
|
||||
return (
|
||||
<Loading visible={loading} style={{ width: '100%' }}>
|
||||
|
@ -266,13 +284,15 @@ class WorkFlowComponent extends Component<Props, State> {
|
|||
</a>
|
||||
</If>
|
||||
<div className="option-item">
|
||||
<Dropdown
|
||||
trigger={<Icon type="ellipsis" className="options-icon" />}
|
||||
triggerType={['click']}
|
||||
className="workflow-more"
|
||||
>
|
||||
{menu}
|
||||
</Dropdown>
|
||||
<If condition={option.edit}>
|
||||
<Dropdown
|
||||
trigger={<Icon type="ellipsis" className="options-icon" />}
|
||||
triggerType={['click']}
|
||||
className="workflow-more"
|
||||
>
|
||||
{menu}
|
||||
</Dropdown>
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -292,4 +312,4 @@ class WorkFlowComponent extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
export default WorkFlowComponent;
|
||||
export default WorkflowComponent;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
.workflow-edge-container {
|
||||
// height: 10px;
|
||||
// background-color: green;
|
||||
// width: 10px;
|
||||
// line-height: 10px;
|
||||
color: white;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: #fff;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
background-color: #1b58f4;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
import { Balloon } from '@b-design/ui';
|
||||
import React, { Component } from 'react';
|
||||
import Translation from '../../../components/Translation';
|
||||
|
||||
import './index.less';
|
||||
export interface EdgeData {
|
||||
dest: string;
|
||||
id: string;
|
||||
src: string;
|
||||
}
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
data?: any;
|
||||
data: EdgeData;
|
||||
editMode?: boolean;
|
||||
addNode: () => void;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
@ -12,14 +21,27 @@ type State = {};
|
|||
class WorkFlowEdge extends Component<Props, State> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {}
|
||||
|
||||
render() {
|
||||
return <div className="workflow-edge-container">{/* + */}</div>;
|
||||
const { editMode } = this.props;
|
||||
if (editMode) {
|
||||
return (
|
||||
<Balloon
|
||||
trigger={
|
||||
<div onClick={this.props.addNode} className="workflow-edge-container">
|
||||
+
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Translation>Click to add Workflow Step</Translation>
|
||||
</Balloon>
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { Message } from '@b-design/ui';
|
||||
import type {
|
||||
DiagramMakerData,
|
||||
DiagramMakerNode,
|
||||
DiagramMakerEdge,
|
||||
DiagramMakerPanel,
|
||||
} from 'diagram-maker';
|
||||
import type { DiagramMakerData, DiagramMakerNode, DiagramMakerEdge } from 'diagram-maker';
|
||||
import {
|
||||
DiagramMaker,
|
||||
ConnectorPlacement,
|
||||
|
@ -17,17 +13,15 @@ import {
|
|||
NodeActions,
|
||||
} from 'diagram-maker';
|
||||
import WorkFlowNode from '../workflow-node';
|
||||
import type { EdgeData } from '../workflow-edge';
|
||||
import WorkFlowEdge from '../workflow-edge';
|
||||
import WorkFlowPannel from '../workflow-panel';
|
||||
import WorkFlowToolTip from '../workflow-tooltip';
|
||||
import type { EdgesAndNodes, WorkFlowNodeType, WorkFlowEdgeType } from '../entity';
|
||||
import { WORKFLOW_COMMON_PANNEL } from '../entity';
|
||||
import WorkflowForm from './workflow-form';
|
||||
import 'diagram-maker/dist/diagramMaker.css';
|
||||
import './index.less';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import type { NodeItem } from '../workflow-component';
|
||||
import type { Definition } from '../../../interface/addon';
|
||||
import type { WorkflowStep } from '../../../interface/application';
|
||||
|
||||
type WorkFlowItemProps = {
|
||||
workflowId: string;
|
||||
|
@ -57,9 +51,9 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
}
|
||||
const { data, edit } = this.props;
|
||||
const containerWidth = this.container.clientWidth;
|
||||
const nodeWidth = Object.keys(data.nodes).length * 200 + 300;
|
||||
const basicePlatformConfig = {
|
||||
panels: WORKFLOW_COMMON_PANNEL,
|
||||
const nodeWidth = Object.keys(data.nodes).length * 360;
|
||||
const basicPlatformConfig = {
|
||||
panels: {},
|
||||
workspace: {
|
||||
position: {
|
||||
x: 0,
|
||||
|
@ -82,20 +76,31 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
const initialData: DiagramMakerData<WorkFlowNodeType, WorkFlowEdgeType> = Object.assign(
|
||||
{},
|
||||
data,
|
||||
basicePlatformConfig,
|
||||
basicPlatformConfig,
|
||||
);
|
||||
const { workFlowDefinitions } = this.props;
|
||||
const nodeTypeConfigs: any = {};
|
||||
workFlowDefinitions.map((definition: Definition) => {
|
||||
nodeTypeConfigs[definition.name] = {
|
||||
const nodeTypeConfigs: any = {
|
||||
prev: {
|
||||
size: {
|
||||
width: 140,
|
||||
height: 40,
|
||||
width: 200,
|
||||
height: 80,
|
||||
},
|
||||
visibleConnectorTypes: VisibleConnectorTypes.OUTPUT_ONLY,
|
||||
},
|
||||
next: {
|
||||
size: {
|
||||
width: 200,
|
||||
height: 80,
|
||||
},
|
||||
visibleConnectorTypes: VisibleConnectorTypes.INPUT_ONLY,
|
||||
},
|
||||
common: {
|
||||
size: {
|
||||
width: 200,
|
||||
height: 80,
|
||||
},
|
||||
definition: definition,
|
||||
visibleConnectorTypes: VisibleConnectorTypes.BOTH,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
this.diagramMaker = new DiagramMaker(
|
||||
this.container,
|
||||
{
|
||||
|
@ -108,24 +113,34 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
ReactDOM.render(
|
||||
<WorkFlowNode
|
||||
id={node.id}
|
||||
data={node.consumerData}
|
||||
data={node.consumerData as WorkflowStep}
|
||||
selected={node.diagramMakerData.selected}
|
||||
workflowId={this.props.workflowId}
|
||||
showDetail={this.showStepDetail}
|
||||
editMode={edit}
|
||||
deleteNode={this.deleteNode}
|
||||
typeId={node.typeId}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
},
|
||||
edge: (edge: DiagramMakerEdge<{}>, container: HTMLElement) => {
|
||||
ReactDOM.render(<WorkFlowEdge id={edge.id} data={edge.consumerData} />, container);
|
||||
ReactDOM.render(
|
||||
<WorkFlowEdge
|
||||
editMode={edit}
|
||||
id={edge.id}
|
||||
addNode={() => {
|
||||
this.newNode(edge.src);
|
||||
}}
|
||||
data={edge as EdgeData}
|
||||
/>,
|
||||
container,
|
||||
);
|
||||
},
|
||||
destroy: (container: HTMLElement) => {
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
},
|
||||
panels: {
|
||||
p1: this.renderLeftPannel,
|
||||
p2: this.renderTopPannel,
|
||||
},
|
||||
panels: {},
|
||||
},
|
||||
nodeTypeConfig: nodeTypeConfigs,
|
||||
actionInterceptor: (action: any, dispatch, getState) => {
|
||||
|
@ -144,27 +159,34 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
}
|
||||
}
|
||||
}
|
||||
dispatch(action);
|
||||
if (action.type == 'NODE_CREATE') {
|
||||
this.setState({
|
||||
currentSelectedNodeData: Object.assign(action.payload, {
|
||||
consumerData: { type: action.payload.typeId },
|
||||
}),
|
||||
visible: true,
|
||||
});
|
||||
if (action.type == NodeActions.NODE_DRAG) {
|
||||
if (action.payload.workspaceRectangle.size.height > 400) {
|
||||
this.diagramMaker.api.resetZoom();
|
||||
}
|
||||
}
|
||||
if (action.type == 'DELETE_ITEMS') {
|
||||
if (Array.isArray(action.payload.edgeIds) && action.payload.edgeIds.length > 0) {
|
||||
Message.warning(
|
||||
'If remove the edge and save the workflow, the nodes that after this edge will be removed.',
|
||||
);
|
||||
}
|
||||
}
|
||||
dispatch(action);
|
||||
},
|
||||
},
|
||||
{
|
||||
initialData,
|
||||
consumerRootReducer: (state: any, action: any) => {
|
||||
switch (action.type) {
|
||||
case 'UPDATENODE':
|
||||
case 'UPDATE_NODE':
|
||||
const newNode: any = {};
|
||||
newNode[action.payload.id] = action.payload;
|
||||
const newNodes = Object.assign({}, state.nodes, newNode);
|
||||
const newState = Object.assign({}, state, { nodes: newNodes });
|
||||
return newState;
|
||||
return Object.assign({}, state, { nodes: newNodes });
|
||||
case 'UPDATE_NODES':
|
||||
return Object.assign({}, state, { nodes: action.payload });
|
||||
case 'UPDATE_EDGES':
|
||||
return Object.assign({}, state, { edges: action.payload });
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -172,24 +194,6 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
eventListener: () => {},
|
||||
},
|
||||
);
|
||||
|
||||
this.diagramMaker.store.subscribe(() => {
|
||||
const { nodes } = this.diagramMaker.store.getState();
|
||||
let currentNode: any = null;
|
||||
Object.keys(nodes).forEach((key) => {
|
||||
const obj = nodes[key];
|
||||
if (obj && obj.diagramMakerData && obj.diagramMakerData.selected) {
|
||||
currentNode = obj;
|
||||
}
|
||||
});
|
||||
const { visible } = this.state;
|
||||
if (currentNode && !visible) {
|
||||
this.setState({
|
||||
currentSelectedNodeData: currentNode,
|
||||
visible: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fit = () => {
|
||||
|
@ -204,66 +208,6 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
});
|
||||
};
|
||||
|
||||
undo = () => {
|
||||
this.diagramMaker.api.undo();
|
||||
};
|
||||
|
||||
redo = () => {
|
||||
this.diagramMaker.api.redo();
|
||||
};
|
||||
|
||||
zoomIn = () => {
|
||||
this.diagramMaker.api.zoomIn();
|
||||
};
|
||||
|
||||
zoomOut = () => {
|
||||
this.diagramMaker.api.zoomOut();
|
||||
};
|
||||
|
||||
renderLeftPannel = (
|
||||
panel: DiagramMakerPanel,
|
||||
state: DiagramMakerData<WorkFlowNodeType, WorkFlowEdgeType>,
|
||||
diagramMakerContainer: HTMLElement,
|
||||
) => {
|
||||
const { workFlowDefinitions, edit } = this.props;
|
||||
const parentContainer = diagramMakerContainer.parentElement;
|
||||
if (parentContainer) {
|
||||
parentContainer.style.display = 'block';
|
||||
}
|
||||
if (!edit && parentContainer) {
|
||||
parentContainer.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
ReactDOM.render(
|
||||
<WorkFlowPannel
|
||||
definitions={workFlowDefinitions}
|
||||
id={panel.id}
|
||||
workflowId={this.props.workflowId}
|
||||
/>,
|
||||
diagramMakerContainer,
|
||||
);
|
||||
};
|
||||
|
||||
renderTopPannel = (
|
||||
panel: DiagramMakerPanel,
|
||||
state: DiagramMakerData<WorkFlowNodeType, WorkFlowEdgeType>,
|
||||
diagramMakerContainer: HTMLElement,
|
||||
) => {
|
||||
ReactDOM.render(
|
||||
<WorkFlowToolTip
|
||||
id={panel.id}
|
||||
edit={this.props.edit}
|
||||
undo={this.undo}
|
||||
redo={this.redo}
|
||||
autoLayout={this.autoLayout}
|
||||
zoomIn={this.zoomIn}
|
||||
zoomOut={this.zoomOut}
|
||||
fit={this.fit}
|
||||
/>,
|
||||
diagramMakerContainer,
|
||||
);
|
||||
};
|
||||
|
||||
componentWillReceiveProps = (nextProps: WorkFlowItemProps) => {
|
||||
if (nextProps.edit !== this.props.edit) {
|
||||
if (nextProps.edit) {
|
||||
|
@ -278,6 +222,142 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
return { edges, nodes };
|
||||
};
|
||||
|
||||
deleteNode = (id: string) => {
|
||||
const { edges, nodes } = this.diagramMaker.store.getState();
|
||||
const newNodes: any = {};
|
||||
const deleteNode = nodes[id];
|
||||
if (!deleteNode) {
|
||||
return;
|
||||
}
|
||||
Object.keys(nodes).map((key: string) => {
|
||||
if (key == id) {
|
||||
return;
|
||||
}
|
||||
const step: NodeItem = nodes[key];
|
||||
let stepNew: NodeItem = Object.assign({}, step);
|
||||
if (stepNew.diagramMakerData.position.x >= deleteNode.diagramMakerData.position.x) {
|
||||
stepNew = Object.assign(stepNew, {
|
||||
diagramMakerData: {
|
||||
selected: stepNew.diagramMakerData.selected,
|
||||
size: stepNew.diagramMakerData.size,
|
||||
position: {
|
||||
x: stepNew.diagramMakerData.position.x - 320,
|
||||
y: stepNew.diagramMakerData.position.y,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
newNodes[stepNew.id] = stepNew;
|
||||
});
|
||||
const newEdges: any = {};
|
||||
let preEdge: EdgeData = { id: '', dest: '', src: '' };
|
||||
let dest = '';
|
||||
Object.keys(edges).map((key: string) => {
|
||||
const edge: EdgeData = edges[key];
|
||||
if (edge.src == id) {
|
||||
dest = edge.dest;
|
||||
return;
|
||||
}
|
||||
if (edge.dest == id) {
|
||||
preEdge = Object.assign({}, edge);
|
||||
return;
|
||||
}
|
||||
newEdges[key] = edge;
|
||||
});
|
||||
preEdge.dest = dest;
|
||||
newEdges[preEdge.id] = preEdge;
|
||||
this.diagramMaker.store.dispatch({
|
||||
type: 'UPDATE_NODES',
|
||||
payload: newNodes,
|
||||
});
|
||||
this.diagramMaker.store.dispatch({
|
||||
type: 'UPDATE_EDGES',
|
||||
payload: newEdges,
|
||||
});
|
||||
};
|
||||
|
||||
newNode = (source: string) => {
|
||||
const { nodes, edges } = this.diagramMaker.store.getState();
|
||||
const sourceNode = nodes[source];
|
||||
let newNode: NodeItem = Object.assign({}, sourceNode);
|
||||
newNode = Object.assign(newNode, {
|
||||
consumerData: { name: '', properties: '', type: '' },
|
||||
id: uuid(),
|
||||
typeId: 'common',
|
||||
diagramMakerData: {
|
||||
selected: newNode.diagramMakerData.selected,
|
||||
size: newNode.diagramMakerData.size,
|
||||
position: {
|
||||
x: newNode.diagramMakerData.position.x + 320,
|
||||
y: newNode.diagramMakerData.position.y,
|
||||
},
|
||||
},
|
||||
});
|
||||
const newNodes: any = {};
|
||||
Object.keys(nodes).map((key: string) => {
|
||||
const step: NodeItem = nodes[key];
|
||||
let stepNew: NodeItem = Object.assign({}, step);
|
||||
if (stepNew.diagramMakerData.position.x >= newNode.diagramMakerData.position.x) {
|
||||
stepNew = Object.assign(stepNew, {
|
||||
diagramMakerData: {
|
||||
selected: stepNew.diagramMakerData.selected,
|
||||
size: stepNew.diagramMakerData.size,
|
||||
position: {
|
||||
x: stepNew.diagramMakerData.position.x + 320,
|
||||
y: stepNew.diagramMakerData.position.y,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
newNodes[stepNew.id] = stepNew;
|
||||
});
|
||||
newNodes[newNode.id] = newNode;
|
||||
|
||||
const newEdges: any = {};
|
||||
Object.keys(edges).map((key: string) => {
|
||||
const edge: EdgeData = edges[key];
|
||||
if (edge.src == source) {
|
||||
const pre = Object.assign({}, edge);
|
||||
const next = Object.assign({}, edge);
|
||||
pre.dest = newNode.id;
|
||||
pre.id = pre.src;
|
||||
next.src = newNode.id;
|
||||
next.id = next.src;
|
||||
newEdges[pre.id] = pre;
|
||||
newEdges[next.id] = next;
|
||||
return;
|
||||
}
|
||||
newEdges[key] = edge;
|
||||
});
|
||||
|
||||
this.diagramMaker.store.dispatch({
|
||||
type: 'UPDATE_NODES',
|
||||
payload: newNodes,
|
||||
});
|
||||
|
||||
this.diagramMaker.store.dispatch({
|
||||
type: 'UPDATE_EDGES',
|
||||
payload: newEdges,
|
||||
});
|
||||
this.setState({
|
||||
currentSelectedNodeData: newNode,
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
showStepDetail = (id: string) => {
|
||||
const { edit } = this.props;
|
||||
if (!edit) {
|
||||
return;
|
||||
}
|
||||
const { nodes } = this.diagramMaker.store.getState();
|
||||
const consumerData: NodeItem = nodes[id];
|
||||
this.setState({
|
||||
currentSelectedNodeData: consumerData,
|
||||
visible: true,
|
||||
});
|
||||
};
|
||||
|
||||
openDrawer = () => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
|
@ -296,20 +376,24 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
createOrUpdateNode = (values: any) => {
|
||||
const { nodes } = this.diagramMaker.store.getState();
|
||||
const { currentSelectedNodeData } = this.state;
|
||||
let consumerData: NodeItem = nodes[currentSelectedNodeData.id];
|
||||
consumerData = Object.assign({}, consumerData, {
|
||||
consumerData: values,
|
||||
diagramMakerData: {
|
||||
selected: false,
|
||||
size: consumerData.diagramMakerData.size,
|
||||
position: consumerData.diagramMakerData.position,
|
||||
},
|
||||
});
|
||||
this.diagramMaker.store.dispatch({
|
||||
type: 'UPDATENODE',
|
||||
payload: consumerData,
|
||||
});
|
||||
this.closeDrawer();
|
||||
if (currentSelectedNodeData) {
|
||||
let consumerData: NodeItem = nodes[currentSelectedNodeData.id];
|
||||
if (consumerData) {
|
||||
consumerData = Object.assign({}, consumerData, {
|
||||
consumerData: values,
|
||||
diagramMakerData: {
|
||||
selected: false,
|
||||
size: consumerData.diagramMakerData.size,
|
||||
position: consumerData.diagramMakerData.position,
|
||||
},
|
||||
});
|
||||
this.diagramMaker.store.dispatch({
|
||||
type: 'UPDATE_NODE',
|
||||
payload: consumerData,
|
||||
});
|
||||
this.closeDrawer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onDelete = (values: NodeItem) => {
|
||||
|
@ -335,7 +419,6 @@ class WorkFlowItem extends Component<WorkFlowItemProps, State> {
|
|||
/>
|
||||
<If condition={visible}>
|
||||
<WorkflowForm
|
||||
onDelete={() => this.onDelete(currentSelectedNodeData)}
|
||||
createOrUpdateNode={this.createOrUpdateNode}
|
||||
data={currentSelectedNodeData}
|
||||
checkStepName={(name: string) => {
|
||||
|
|
|
@ -21,13 +21,12 @@ import locale from '../../../utils/locale';
|
|||
|
||||
type Props = {
|
||||
createOrUpdateNode: (data: any) => void;
|
||||
data: DiagramMakerNode<WorkFlowNodeType>;
|
||||
data?: DiagramMakerNode<WorkFlowNodeType>;
|
||||
workFlowDefinitions: [];
|
||||
appName?: string;
|
||||
componentName?: string;
|
||||
closeDrawer: () => void;
|
||||
checkStepName: (name: string) => boolean;
|
||||
onDelete: () => void;
|
||||
dispatch?: ({}) => {};
|
||||
t: (key: string) => {};
|
||||
};
|
||||
|
@ -56,14 +55,16 @@ class WorkflowForm extends Component<Props, State> {
|
|||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
const { consumerData } = this.props.data;
|
||||
this.field.setValues(consumerData || '');
|
||||
let properties = consumerData && consumerData.properties;
|
||||
if (properties && typeof properties === 'string') {
|
||||
properties = JSON.parse(properties);
|
||||
if (this.props.data) {
|
||||
const { consumerData } = this.props.data;
|
||||
this.field.setValues(consumerData || '');
|
||||
let properties = consumerData && consumerData.properties;
|
||||
if (properties && typeof properties === 'string') {
|
||||
properties = JSON.parse(properties);
|
||||
}
|
||||
this.field.setValues({ properties: properties });
|
||||
this.onDetailsComponeDefinition((consumerData && consumerData.type) || '');
|
||||
}
|
||||
this.field.setValues({ properties: properties });
|
||||
this.onDetailsComponeDefinition((consumerData && consumerData.type) || '');
|
||||
};
|
||||
|
||||
setValues = (values: any | null) => {
|
||||
|
@ -83,13 +84,6 @@ class WorkflowForm extends Component<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
onDelete = () => {
|
||||
const { onDelete } = this.props;
|
||||
if (onDelete) {
|
||||
onDelete();
|
||||
}
|
||||
};
|
||||
|
||||
transDefinitions() {
|
||||
const { workFlowDefinitions } = this.props;
|
||||
return (workFlowDefinitions || []).map((item: { name: string }) => ({
|
||||
|
@ -129,20 +123,22 @@ class WorkflowForm extends Component<Props, State> {
|
|||
};
|
||||
|
||||
const checkWorkflowStepName = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||
const { consumerData } = this.props.data;
|
||||
if (checkStepName(value)) {
|
||||
if (consumerData?.name && value == consumerData?.name) {
|
||||
callback();
|
||||
return;
|
||||
if (data) {
|
||||
const { consumerData } = data;
|
||||
if (checkStepName(value)) {
|
||||
if (consumerData?.name && value == consumerData?.name) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
callback('name is exist');
|
||||
}
|
||||
callback('name is exist');
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
const edit = data && data.consumerData?.name != undefined && data.consumerData?.name != '';
|
||||
return (
|
||||
<DrawerWithFooter
|
||||
title={<Translation>Edit Workflow Step</Translation>}
|
||||
title={<Translation>{edit ? 'Edit Workflow Step' : 'Add Workflow Step'}</Translation>}
|
||||
placement="right"
|
||||
width={800}
|
||||
onClose={closeDrawer}
|
||||
|
@ -151,26 +147,12 @@ class WorkflowForm extends Component<Props, State> {
|
|||
<Button key={'cancel'} style={{ marginRight: '16px' }} onClick={closeDrawer}>
|
||||
Cancel
|
||||
</Button>,
|
||||
<Button
|
||||
key={'delete'}
|
||||
style={{ marginRight: '16px' }}
|
||||
onClick={() => {
|
||||
this.onDelete();
|
||||
}}
|
||||
title="Delete this step"
|
||||
>
|
||||
Delete
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form field={this.field}>
|
||||
<Row>
|
||||
<Col span={24} style={{ padding: '0 8px' }}>
|
||||
<FormItem
|
||||
label={<Translation>Workflow Type</Translation>}
|
||||
required
|
||||
disabled={this.field.getValue('type')}
|
||||
>
|
||||
<FormItem label={<Translation>Workflow Type</Translation>} required disabled={edit}>
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
className="select"
|
||||
|
@ -202,7 +184,7 @@ class WorkflowForm extends Component<Props, State> {
|
|||
maxLength={32}
|
||||
placeholder={t('Please enter').toString()}
|
||||
{...init('name', {
|
||||
initValue: data.consumerData?.type,
|
||||
initValue: data && data.consumerData?.type,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
|
@ -279,6 +261,7 @@ class WorkflowForm extends Component<Props, State> {
|
|||
})}
|
||||
uiSchema={definitionDetail && definitionDetail.uiSchema}
|
||||
ref={this.uiSchemaRef}
|
||||
mode={this.props.data ? 'edit' : 'new'}
|
||||
/>
|
||||
</FormItem>
|
||||
</If>
|
||||
|
|
|
@ -1,63 +1,27 @@
|
|||
@primarycolor: #1b58f4;
|
||||
.workflow-node-container {
|
||||
width: auto;
|
||||
height: 40px;
|
||||
width: 200px;
|
||||
height: 80px;
|
||||
padding: 16px;
|
||||
color: black;
|
||||
line-height: 40px;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
&.active {
|
||||
border: 1px solid @primarycolor;
|
||||
}
|
||||
}
|
||||
|
||||
.workflow-switch-node-container {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: none;
|
||||
&.active {
|
||||
border: none;
|
||||
.rhombus-container {
|
||||
border: 1px solid @primarycolor;
|
||||
.workflow-step-type {
|
||||
padding-top: 8px;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.workflow-step-delete {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
:hover {
|
||||
color: @primarycolor;
|
||||
}
|
||||
}
|
||||
.rhombus-container {
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
left: 11px;
|
||||
width: 57px;
|
||||
height: 57px;
|
||||
border: 1px solid #ccc;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.start-connector {
|
||||
position: absolute;
|
||||
top: 31px;
|
||||
left: -4px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: blue;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.end-connector {
|
||||
position: absolute;
|
||||
top: 31px;
|
||||
right: -4px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: blue;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
import React, { Component } from 'react';
|
||||
import { DiagramMakerComponents } from 'diagram-maker';
|
||||
import { Icon } from '@b-design/ui';
|
||||
import './index.less';
|
||||
import type { WorkflowStep } from '../../../interface/application';
|
||||
import { Balloon, Icon } from '@b-design/ui';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import Translation from '../../../components/Translation';
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
typeId?: string;
|
||||
workflowId: string;
|
||||
selected?: boolean;
|
||||
data?: any;
|
||||
showDetail: (id: string) => void;
|
||||
deleteNode: (id: string) => void;
|
||||
data?: WorkflowStep;
|
||||
editMode?: boolean;
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
@ -17,69 +22,66 @@ type State = {};
|
|||
class WorkFlowNode extends Component<Props, State> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {}
|
||||
|
||||
render() {
|
||||
const { data = {}, selected, id, workflowId, typeId } = this.props;
|
||||
const { data, selected, id, workflowId, editMode, typeId } = this.props;
|
||||
const showName =
|
||||
data && data.name ? (
|
||||
data.alias || data.name
|
||||
) : (
|
||||
<Translation>Click to config Workflow Step</Translation>
|
||||
);
|
||||
if (typeId == 'prev' || typeId == 'next') {
|
||||
return <React.Fragment />;
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<If condition={typeId !== 'switch'}>
|
||||
<div
|
||||
className={selected ? 'workflow-node-container active' : 'workflow-node-container'}
|
||||
id={id}
|
||||
workflow-id={workflowId}
|
||||
>
|
||||
{data.alias || data.name || <Translation>Click Edit</Translation>}
|
||||
<div
|
||||
data-event-target="true"
|
||||
data-dropzone="true"
|
||||
data-type={DiagramMakerComponents.NODE_CONNECTOR}
|
||||
data-id={id}
|
||||
/>
|
||||
<div
|
||||
data-event-target="true"
|
||||
data-draggable="true"
|
||||
data-type={DiagramMakerComponents.NODE_CONNECTOR}
|
||||
data-id={id}
|
||||
/>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={typeId === 'switch'}>
|
||||
<div
|
||||
className={
|
||||
selected
|
||||
? 'workflow-node-container workflow-switch-node-container active'
|
||||
: 'workflow-node-container workflow-switch-node-container'
|
||||
}
|
||||
id={id}
|
||||
workflow-id={workflowId}
|
||||
title={data.text}
|
||||
>
|
||||
<div className="rhombus-container" />
|
||||
<div className="content">
|
||||
{data.text || <Translation>Click Edit</Translation>}
|
||||
<Icon type="ashbin1" />
|
||||
<div
|
||||
className={selected ? 'workflow-node-container active' : 'workflow-node-container'}
|
||||
id={id}
|
||||
style={{
|
||||
cursor: editMode ? 'pointer' : 'auto',
|
||||
}}
|
||||
onClick={() => this.props.showDetail(id)}
|
||||
workflow-id={workflowId}
|
||||
>
|
||||
<If condition={editMode}>
|
||||
<div className="workflow-step-delete">
|
||||
<Balloon
|
||||
trigger={
|
||||
<Icon
|
||||
size={14}
|
||||
onClick={(event: any) => {
|
||||
event.stopPropagation();
|
||||
this.props.deleteNode(id);
|
||||
}}
|
||||
type="delete"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Translation>Click to remove Workflow Step</Translation>
|
||||
</Balloon>
|
||||
</div>
|
||||
<div
|
||||
className="start-connector"
|
||||
data-event-target="true"
|
||||
data-dropzone="true"
|
||||
data-type={DiagramMakerComponents.NODE_CONNECTOR}
|
||||
data-id={id}
|
||||
/>
|
||||
<div
|
||||
className="end-connector"
|
||||
data-event-target="true"
|
||||
data-draggable="true"
|
||||
data-type={DiagramMakerComponents.NODE_CONNECTOR}
|
||||
data-id={id}
|
||||
/>
|
||||
</div>
|
||||
</If>
|
||||
</If>
|
||||
{showName}
|
||||
<div className="workflow-step-type">{data?.type}</div>
|
||||
<div
|
||||
data-event-target="true"
|
||||
data-dropzone="false"
|
||||
data-type={DiagramMakerComponents.NODE_CONNECTOR}
|
||||
data-id={id}
|
||||
/>
|
||||
<div
|
||||
data-event-target="true"
|
||||
data-draggable="false"
|
||||
data-type={DiagramMakerComponents.NODE_CONNECTOR}
|
||||
data-id={id}
|
||||
/>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.workflow-pannel-container {
|
||||
height: 230px;
|
||||
padding: 20px;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
border: 1px solid #eee;
|
||||
.hl {
|
||||
|
|
|
@ -86,19 +86,23 @@ class AddClustDialog extends React.Component<Props, State> {
|
|||
dashboardURL: values.dashboardURL,
|
||||
kubeConfig: values.kubeConfig,
|
||||
labels: cluster.labels,
|
||||
}).then(() => {
|
||||
Message.success(<Translation>cluster update success</Translation>);
|
||||
this.resetField();
|
||||
this.props.onOK();
|
||||
}).then((re: any) => {
|
||||
if (re) {
|
||||
Message.success(<Translation>cluster update success</Translation>);
|
||||
this.resetField();
|
||||
this.props.onOK();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.props.dispatch({
|
||||
type: 'clusters/createCluster',
|
||||
payload: values,
|
||||
callback: () => {
|
||||
Message.success(<Translation>cluster add success</Translation>);
|
||||
this.resetField();
|
||||
this.props.onOK();
|
||||
callback: (re: any) => {
|
||||
if (re) {
|
||||
Message.success(<Translation>cluster add success</Translation>);
|
||||
this.resetField();
|
||||
this.props.onOK();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -164,7 +168,7 @@ class AddClustDialog extends React.Component<Props, State> {
|
|||
onOk={this.onOk}
|
||||
onCancel={this.onClose}
|
||||
onClose={this.onClose}
|
||||
footerActions={['ok', 'cancel']}
|
||||
footerActions={['cancel', 'ok']}
|
||||
footerAlign="center"
|
||||
>
|
||||
<Loading visible={editClusterName && !editMode ? true : false} style={{ width: '100%' }}>
|
||||
|
|
|
@ -14,7 +14,8 @@ type Props = {
|
|||
setVisible: (visible: boolean) => void;
|
||||
setCloudService: (isCloudService: boolean) => void;
|
||||
t: (key: string) => string;
|
||||
dispatch: ({}) => void;
|
||||
onPropsOK: () => void;
|
||||
dispatch: ({ }) => void;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -76,6 +77,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
.then((re) => {
|
||||
if (re) {
|
||||
this.setState({ choseInput: false, cloudClusters: re.clusters, btnLoading: false });
|
||||
this.props.onPropsOK();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -122,7 +124,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
});
|
||||
}
|
||||
})
|
||||
.then(() => {});
|
||||
.then(() => { });
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -9,12 +9,14 @@ import { Button, Message } from '@b-design/ui';
|
|||
import { deleteCluster } from '../../api/cluster';
|
||||
import Translation from '../../components/Translation';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import { getEnabledAddons, getAddonsList } from '../../api/addons';
|
||||
import { Addon } from '../../interface/addon';
|
||||
|
||||
type Props = {
|
||||
clusterList: [];
|
||||
defaultCluster: string;
|
||||
cloudClusters: [];
|
||||
dispatch: ({}) => {};
|
||||
dispatch: ({ }) => {};
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -24,6 +26,8 @@ type State = {
|
|||
showAddCluster: boolean;
|
||||
showAddCloudCluster: boolean;
|
||||
editClusterName: string;
|
||||
addonMessage: { name: string; path: string }[];
|
||||
isShowAddonMessage: boolean;
|
||||
};
|
||||
|
||||
@connect((store: any) => {
|
||||
|
@ -39,6 +43,8 @@ class Cluster extends React.Component<Props, State> {
|
|||
showAddCluster: false,
|
||||
showAddCloudCluster: false,
|
||||
editClusterName: '',
|
||||
addonMessage: [],
|
||||
isShowAddonMessage: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -78,10 +84,66 @@ class Cluster extends React.Component<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
onGetEnabledAddon = async () => {
|
||||
getEnabledAddons({}).then((res) => {
|
||||
this.onGetAddonsList(res.enabledAddons);
|
||||
});
|
||||
};
|
||||
|
||||
onGetAddonsList = async (enableAddon: { name: string; phase: boolean }[]) => {
|
||||
getAddonsList({}).then((res) => {
|
||||
const addonMessage: { name: string; path: string }[] = [];
|
||||
const addonList: Addon[] = res.addons;
|
||||
(enableAddon || []).forEach((ele: { name: string; phase: boolean }) => {
|
||||
addonList.forEach((item: Addon) => {
|
||||
const isMatchName = ele.name === item.name;
|
||||
const deploy = item.deployTo || { runtimeCluster: false, runtime_cluster: false };
|
||||
if (isMatchName && (deploy.runtimeCluster || deploy.runtime_cluster)) {
|
||||
addonMessage.push({ name: item.name, path: item.url || '' });
|
||||
}
|
||||
});
|
||||
});
|
||||
if (addonMessage && addonMessage.length !== 0) {
|
||||
this.setState({
|
||||
isShowAddonMessage: true,
|
||||
addonMessage: addonMessage,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
showAddonMessage() {
|
||||
const { addonMessage } = this.state;
|
||||
return (addonMessage || []).map((item, index, arr) => {
|
||||
const lastIndex = arr.length - 1;
|
||||
const showSymbol = index === lastIndex ? '' : '、';
|
||||
return (
|
||||
<span>
|
||||
<a href={item.path}>
|
||||
{item.name}
|
||||
{showSymbol}
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
handleHiddenAddonMessage = () => {
|
||||
this.setState({ isShowAddonMessage: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { clusterList = [], dispatch } = this.props;
|
||||
const { page, pageSize, query, showAddCluster, showAddCloudCluster, editClusterName } =
|
||||
this.state;
|
||||
const {
|
||||
page,
|
||||
pageSize,
|
||||
query,
|
||||
showAddCluster,
|
||||
showAddCloudCluster,
|
||||
editClusterName,
|
||||
isShowAddonMessage,
|
||||
addonMessage,
|
||||
} = this.state;
|
||||
return (
|
||||
<div>
|
||||
<Title
|
||||
|
@ -103,6 +165,14 @@ class Cluster extends React.Component<Props, State> {
|
|||
</Button>,
|
||||
]}
|
||||
/>
|
||||
|
||||
<If condition={isShowAddonMessage && addonMessage.length != 0}>
|
||||
<Message type="notice" closeable onClose={this.handleHiddenAddonMessage}>
|
||||
Connect Cluster Success! Please upgrade {this.showAddonMessage()} addons, make them take
|
||||
effect in the new cluster.
|
||||
</Message>
|
||||
</If>
|
||||
|
||||
<SelectSearch
|
||||
dispatch={dispatch}
|
||||
getChildCompentQuery={(q: string): any => {
|
||||
|
@ -127,6 +197,9 @@ class Cluster extends React.Component<Props, State> {
|
|||
setCloudService={(visible) => {
|
||||
this.setState({ showAddCloudCluster: visible });
|
||||
}}
|
||||
onPropsOK={() => {
|
||||
this.onGetEnabledAddon();
|
||||
}}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</If>
|
||||
|
@ -143,6 +216,7 @@ class Cluster extends React.Component<Props, State> {
|
|||
}}
|
||||
onOK={() => {
|
||||
this.getClusterList();
|
||||
this.onGetEnabledAddon();
|
||||
this.setState({ showAddCluster: false, editClusterName: '' });
|
||||
}}
|
||||
dispatch={dispatch}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
.namespaceDialogWraper {
|
||||
width: 800px;
|
||||
height: 300px;
|
||||
.next-dialog-footer {
|
||||
display: flex;
|
||||
justify-content: center !important;
|
||||
.next-btn {
|
||||
display: block;
|
||||
flex: none !important;
|
||||
width: 100px !important;
|
||||
}
|
||||
}
|
||||
.next-dialog-body {
|
||||
width: 100% !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
.basic {
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
import React from 'react';
|
||||
import { Input, Select, Button, Message } from '@b-design/ui';
|
||||
import { Input, Select, Button, Message, Dialog, Form, Field, Grid } from '@b-design/ui';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import Translation from '../../../../components/Translation';
|
||||
import { createClusterNamespace } from '../../../../api/cluster';
|
||||
import locale from '../../../../utils/locale';
|
||||
import './index.less';
|
||||
|
||||
type Props = {
|
||||
cluster?: string;
|
||||
|
@ -13,6 +14,8 @@ type Props = {
|
|||
loadNamespaces: (cluster: string) => void;
|
||||
disableNew?: boolean;
|
||||
disabled?: boolean;
|
||||
createNamespaceDialog?: boolean;
|
||||
targetField: Field;
|
||||
};
|
||||
|
||||
export interface NamespaceItem {
|
||||
|
@ -21,101 +24,137 @@ export interface NamespaceItem {
|
|||
}
|
||||
|
||||
type State = {
|
||||
showNameSpaceInput: boolean;
|
||||
inputNamespaceParam: string;
|
||||
loading: boolean;
|
||||
createNamespace?: string;
|
||||
visible: boolean;
|
||||
};
|
||||
|
||||
class Namespace extends React.Component<Props, State> {
|
||||
field: Field;
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showNameSpaceInput: false,
|
||||
inputNamespaceParam: '',
|
||||
loading: false,
|
||||
visible: false,
|
||||
};
|
||||
this.field = new Field(this);
|
||||
}
|
||||
|
||||
openNamespaceInput = () => {
|
||||
this.setState({
|
||||
showNameSpaceInput: true,
|
||||
});
|
||||
};
|
||||
|
||||
closeNamespaceInput = () => {
|
||||
this.setState({
|
||||
showNameSpaceInput: false,
|
||||
});
|
||||
const { createNamespaceDialog } = this.props;
|
||||
if (createNamespaceDialog) {
|
||||
this.field.setValue('namespace', '');
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
createNamespace = () => {
|
||||
const { cluster, loadNamespaces } = this.props;
|
||||
const { cluster, loadNamespaces, targetField } = this.props;
|
||||
if (!cluster) {
|
||||
Message.warning('Please select a cluster first');
|
||||
return;
|
||||
}
|
||||
const { createNamespace } = this.state;
|
||||
const namespace = createNamespace || this.field.getValue('namespace');
|
||||
this.setState({ loading: true });
|
||||
if (cluster && createNamespace) {
|
||||
createClusterNamespace({ cluster: cluster, namespace: createNamespace }).then((re) => {
|
||||
if (cluster && namespace) {
|
||||
createClusterNamespace({ cluster: cluster, namespace: namespace }).then((re) => {
|
||||
if (re) {
|
||||
Message.success('create namespace success');
|
||||
loadNamespaces(cluster);
|
||||
}
|
||||
targetField.setValue('runtimeNamespace', namespace);
|
||||
this.setState({
|
||||
loading: false,
|
||||
showNameSpaceInput: false,
|
||||
visible: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onClose = () => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { disableNew, onChange, namespaces, value, disabled } = this.props;
|
||||
const { showNameSpaceInput, loading } = this.state;
|
||||
const { visible } = this.state;
|
||||
const { Col, Row } = Grid;
|
||||
const init = this.field.init;
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
fixedSpan: 10,
|
||||
},
|
||||
wrapperCol: {
|
||||
span: 14,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<If condition={!showNameSpaceInput}>
|
||||
<div className="cluster-container">
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
className="cluster-params-input"
|
||||
mode="single"
|
||||
disabled={disabled}
|
||||
dataSource={namespaces}
|
||||
onChange={onChange}
|
||||
placeholder={''}
|
||||
value={value}
|
||||
/>
|
||||
<If condition={!disableNew}>
|
||||
<Button
|
||||
className="cluster-option-btn"
|
||||
type="secondary"
|
||||
disabled={disabled}
|
||||
onClick={this.openNamespaceInput}
|
||||
>
|
||||
<Translation>New</Translation>
|
||||
</Button>
|
||||
</If>
|
||||
</div>
|
||||
</If>
|
||||
<If condition={showNameSpaceInput}>
|
||||
<div className="cluster-container">
|
||||
<Input
|
||||
onChange={(v) => this.setState({ createNamespace: v })}
|
||||
className="cluster-params-input"
|
||||
/>
|
||||
<div className="cluster-container">
|
||||
<Select
|
||||
locale={locale.Select}
|
||||
className="cluster-params-input"
|
||||
mode="single"
|
||||
disabled={disabled}
|
||||
dataSource={namespaces}
|
||||
onChange={onChange}
|
||||
placeholder={''}
|
||||
value={value}
|
||||
/>
|
||||
<If condition={!disableNew}>
|
||||
<Button
|
||||
loading={loading}
|
||||
className="cluster-option-btn"
|
||||
type="secondary"
|
||||
onClick={this.createNamespace}
|
||||
disabled={disabled}
|
||||
onClick={this.openNamespaceInput}
|
||||
>
|
||||
<Translation>Submit</Translation>
|
||||
<Translation>New</Translation>
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
locale={locale.Dialog}
|
||||
className={'namespaceDialogWraper'}
|
||||
title={<Translation>Create Namespace</Translation>}
|
||||
autoFocus={true}
|
||||
isFullScreen={true}
|
||||
visible={visible}
|
||||
onOk={this.createNamespace}
|
||||
onCancel={this.onClose}
|
||||
onClose={this.onClose}
|
||||
footerActions={['cancel', 'ok']}
|
||||
footerAlign="center"
|
||||
>
|
||||
<Form field={this.field} {...formItemLayout}>
|
||||
<Row>
|
||||
<Col span={24} style={{ padding: '0 8px' }}>
|
||||
<Form.Item label={<Translation>namespace</Translation>} required>
|
||||
<Input
|
||||
name="namespace"
|
||||
placeholder={'Please enter'}
|
||||
{...init('namespace', {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please select namesapce',
|
||||
},
|
||||
],
|
||||
})}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -312,6 +312,8 @@ class DeliveryDialog extends React.Component<Props, State> {
|
|||
namespaces={namespaces}
|
||||
loadNamespaces={this.loadNamespaces}
|
||||
cluster={cluster}
|
||||
createNamespaceDialog={true}
|
||||
targetField={this.field}
|
||||
/>
|
||||
</FormItem>
|
||||
</Col>
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -1993,6 +1993,11 @@
|
|||
resolved "https://registry.nlark.com/@types/unist/download/@types/unist-2.0.6.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Funist%2Fdownload%2F%40types%2Funist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
|
||||
integrity sha1-JQp7FsO5H2cqJFUuxkZ47rHToI0=
|
||||
|
||||
"@types/uuid@^8.3.4":
|
||||
version "8.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
||||
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
|
||||
|
||||
"@types/webpack-sources@*":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.nlark.com/@types/webpack-sources/download/@types/webpack-sources-3.2.0.tgz?cache=0&sync_timestamp=1629709718286&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40types%2Fwebpack-sources%2Fdownload%2F%40types%2Fwebpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b"
|
||||
|
@ -12210,6 +12215,11 @@ uuid@^3.3.2, uuid@^3.4.0:
|
|||
resolved "https://registry.npmmirror.com/uuid/download/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4=
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
uvu@^0.5.0:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.npmmirror.com/uvu/download/uvu-0.5.2.tgz#c145e7f4b5becf80099cf22fd8a4a05f0112b2c0"
|
||||
|
|
Loading…
Reference in New Issue