mirror of https://github.com/kubevela/velaux.git
Compare commits
11 Commits
Author | SHA1 | Date |
---|---|---|
|
1bf3b192e5 | |
|
44c43de6ec | |
|
ed6d93c5cc | |
|
510bc7d8ec | |
|
2e0b738321 | |
|
8dfeae2cc6 | |
|
171c490d7c | |
|
328a212da8 | |
|
c9006c5faf | |
|
44facaa92a | |
|
0275634270 |
|
@ -40,6 +40,7 @@ jobs:
|
|||
push: true
|
||||
build-args: |
|
||||
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
|
||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||
tags: |-
|
||||
acr.kubevela.net/oamdev/velaux:${{ steps.get_version.outputs.VERSION }}
|
||||
oamdev/velaux:${{ steps.get_version.outputs.VERSION }}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
FROM node:16-alpine as builder
|
||||
ARG VERSION
|
||||
WORKDIR /app/velaux
|
||||
ADD . .
|
||||
ENV VERSION=${VERSION}
|
||||
RUN apk add --no-cache git && yarn install && yarn build
|
||||
RUN rm -rf /app/velaux/build/mock
|
||||
|
||||
|
|
|
@ -89,6 +89,9 @@ function getClientEnvironment(publicUrl) {
|
|||
// which is why it's disabled by default.
|
||||
// It is defined here so it is available in the webpackHotDevClient.
|
||||
FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
|
||||
|
||||
// The release version
|
||||
VERSION: process.env.VERSION,
|
||||
},
|
||||
);
|
||||
// Stringify all values so we can feed into webpack DefinePlugin
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "valaux",
|
||||
"version": "1.4.0-beta.2",
|
||||
"version": "1.4.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
|
@ -52,6 +52,7 @@
|
|||
"dva": "2.4.1",
|
||||
"dva-core": "2.0.4",
|
||||
"dva-loading": "3.0.22",
|
||||
"github-markdown-css": "^5.1.0",
|
||||
"i18next": "^19.8.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash": "4.17.21",
|
||||
|
@ -67,6 +68,7 @@
|
|||
"react-dnd-html5-backend": "^7.2.0",
|
||||
"react-dom": "^16.3.0",
|
||||
"react-i18next": "11.13.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-markdown": "7.1.0",
|
||||
"react-refresh": "^0.10.0",
|
||||
"redux": "4.1.2",
|
||||
|
@ -86,7 +88,6 @@
|
|||
"@pmmmwh/react-refresh-webpack-plugin": "0.5.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@types/chai": "^4.2.11",
|
||||
"dagre-compound": "0.0.11",
|
||||
"@types/js-yaml": "^4.0.1",
|
||||
"@types/lodash": "^4.14.176",
|
||||
"@types/mocha": "^8.2.1",
|
||||
|
@ -104,6 +105,7 @@
|
|||
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
||||
"chai": "^4.2.0",
|
||||
"css-loader": "5.2.7",
|
||||
"dagre-compound": "0.0.11",
|
||||
"dotenv": "10.0.0",
|
||||
"dotenv-expand": "5.1.0",
|
||||
"eslint": "^7.11.0",
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/icon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
|
|
|
@ -116,6 +116,11 @@ export function getPolicyDetail(params: { appName: string; policyName: string })
|
|||
return get(gurl, params).then((res) => res);
|
||||
}
|
||||
|
||||
export function deletePolicy(params: { appName: string; policyName: string }) {
|
||||
const gurl = `${application}/${params.appName}/policies/${params.policyName}`;
|
||||
return rdelete(gurl, params).then((res) => res);
|
||||
}
|
||||
|
||||
export function createApplicationTemplate(params: any) {
|
||||
const gurl = isMock
|
||||
? `${createApplicationTemplate_mock}`
|
||||
|
|
|
@ -45,3 +45,13 @@ export function getChartRepos(params: { project?: string }) {
|
|||
}
|
||||
return get(url, { params: params }).then((res) => res);
|
||||
}
|
||||
|
||||
export function getImageRepos(params: { project: string }) {
|
||||
const url = baseURL + repository + '/image/repos';
|
||||
return get(url, { params: params }).then((res) => res);
|
||||
}
|
||||
|
||||
export function getImageInfo(params: { project: string; secretName?: string; name: string }) {
|
||||
const url = baseURL + repository + '/image/info';
|
||||
return get(url, { params: params }).then((res) => res);
|
||||
}
|
||||
|
|
|
@ -485,8 +485,8 @@ a {
|
|||
border: @dangerColor 1px solid !important;
|
||||
}
|
||||
.danger-btn:hover {
|
||||
background: @dangerColor !important;
|
||||
color: #fff !important;
|
||||
background: @dangerColor !important;
|
||||
}
|
||||
.danger-icon:hover {
|
||||
color: @dangerColor !important;
|
||||
|
|
|
@ -9,9 +9,9 @@ type Props = {
|
|||
language: string;
|
||||
fileUrl?: string;
|
||||
runtime?: any;
|
||||
'data-meta'?: string;
|
||||
id?: string;
|
||||
onChange?: (params: any) => void;
|
||||
onBlurEditor?: (value: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
|
@ -25,6 +25,7 @@ class DefinitionCode extends React.Component<Props> {
|
|||
language,
|
||||
readOnly,
|
||||
onChange,
|
||||
onBlurEditor,
|
||||
fileUrl = `//b.txt`,
|
||||
} = this.props;
|
||||
const container: any = document.getElementById(containerId);
|
||||
|
@ -55,6 +56,12 @@ class DefinitionCode extends React.Component<Props> {
|
|||
if (onChange) {
|
||||
this.editor.onDidChangeModelContent(() => onChange(textModel.getValue()));
|
||||
}
|
||||
if (onBlurEditor) {
|
||||
this.editor.onDidBlurEditorText(() => {
|
||||
onBlurEditor(textModel.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
monaco.editor.setModelLanguage(textModel, language);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.title-wraper {
|
||||
.title-wrapper {
|
||||
height: 45px;
|
||||
.title {
|
||||
font-weight: 500;
|
||||
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
|
||||
@media (max-width: 719px) and (min-width: 480px) {
|
||||
.title-wraper {
|
||||
.title-wrapper {
|
||||
.subTitle {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ export default function (props: Props) {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Row className="title-wraper">
|
||||
<Col span="15">
|
||||
<Row className="title-wrapper" wrap={true}>
|
||||
<Col xl={15} xs={24}>
|
||||
<span className="title font-size-20">
|
||||
<Translation>{title}</Translation>
|
||||
</span>
|
||||
|
@ -26,7 +26,7 @@ export default function (props: Props) {
|
|||
<Translation>{subTitle}</Translation>
|
||||
</span>
|
||||
</Col>
|
||||
<Col span="9">
|
||||
<Col xl={9} xs={24}>
|
||||
<div className="float-right">
|
||||
{extButtons &&
|
||||
extButtons.map((item) => {
|
||||
|
|
|
@ -15,7 +15,6 @@ const Permission = (props: Props) => {
|
|||
if (!props.userInfo) {
|
||||
return null;
|
||||
}
|
||||
console.log(props.request);
|
||||
if (!checkPermission(props.request, props.project, props.userInfo)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -19,14 +19,18 @@
|
|||
|
||||
.name {
|
||||
display: flex;
|
||||
flex: 1 1;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
line-height: 40px;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
.kind {
|
||||
margin: 0;
|
||||
color: #a6a6a6;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
@ -57,15 +61,17 @@
|
|||
}
|
||||
.additional {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: -12px;
|
||||
display: block;
|
||||
right:8px
|
||||
}
|
||||
}
|
||||
|
||||
.graph-node-app {
|
||||
padding-left: 5em;
|
||||
|
||||
.name {
|
||||
line-height: 40px;
|
||||
}
|
||||
.icon {
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
|
@ -87,7 +93,13 @@
|
|||
|
||||
.graph-node-pod {
|
||||
.icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 60px;
|
||||
span {
|
||||
color: #a6a6a6;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
line-height: 30px;
|
||||
|
@ -116,7 +128,7 @@
|
|||
transition: all 0.2s linear;
|
||||
|
||||
&:last-child {
|
||||
&:after {
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
color: #a3a3a3;
|
||||
|
@ -127,4 +139,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.line {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import pod from '../../assets/resources/pod.svg';
|
|||
import kubevela from '../../assets/KubeVela-01.svg';
|
||||
|
||||
import { Link } from 'dva/router';
|
||||
import { Dropdown, Icon, Menu, Tag } from '@b-design/ui';
|
||||
import { Dropdown, Icon, Menu, Tag, Balloon } from '@b-design/ui';
|
||||
import i18n from '../../i18n';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
|
||||
|
@ -60,7 +60,7 @@ export interface TreeNode {
|
|||
|
||||
function renderResourceNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
||||
const fullName = nodeKey(node);
|
||||
return (
|
||||
const graphNode = (
|
||||
<div
|
||||
key={id}
|
||||
onClick={() => props.onNodeClick && props.onNodeClick(fullName)}
|
||||
|
@ -68,7 +68,6 @@ function renderResourceNode(props: TreeGraphProps, id: string, node: GraphNode)
|
|||
'error-status': node.resource.healthStatus?.statusCode == 'UnHealthy',
|
||||
'warning-status': node.resource.healthStatus?.statusCode == 'Progressing',
|
||||
})}
|
||||
title={describeNode(node)}
|
||||
style={{
|
||||
left: node.x,
|
||||
top: node.y,
|
||||
|
@ -81,7 +80,8 @@ function renderResourceNode(props: TreeGraphProps, id: string, node: GraphNode)
|
|||
<ResourceIcon kind={node.resource.kind || ''} />
|
||||
</div>
|
||||
<div className={classNames('name')}>
|
||||
<span>{node.resource.name}</span>
|
||||
<div>{node.resource.name}</div>
|
||||
<div className="kind">{node.resource.kind}</div>
|
||||
</div>
|
||||
<div className={classNames('actions')}>
|
||||
<Dropdown trigger={<Icon type="ellipsis-vertical" />}>
|
||||
|
@ -99,17 +99,25 @@ function renderResourceNode(props: TreeGraphProps, id: string, node: GraphNode)
|
|||
</If>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Balloon trigger={graphNode}>
|
||||
<div>
|
||||
{describeNode(node).map((line) => {
|
||||
return <p className="line">{line}</p>;
|
||||
})}
|
||||
</div>
|
||||
</Balloon>
|
||||
);
|
||||
}
|
||||
|
||||
function renderAppNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
||||
const fullName = nodeKey(node);
|
||||
|
||||
return (
|
||||
const graphNode = (
|
||||
<div
|
||||
key={id}
|
||||
onClick={() => props.onNodeClick && props.onNodeClick(fullName)}
|
||||
className={classNames('graph-node', 'graph-node-app')}
|
||||
title={describeNode(node)}
|
||||
style={{
|
||||
left: node.x,
|
||||
top: node.y,
|
||||
|
@ -126,12 +134,21 @@ function renderAppNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Balloon trigger={graphNode}>
|
||||
<div>
|
||||
{describeNode(node).map((line) => {
|
||||
return <p className="line">{line}</p>;
|
||||
})}
|
||||
</div>
|
||||
</Balloon>
|
||||
);
|
||||
}
|
||||
|
||||
function renderPodNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
||||
const fullName = nodeKey(node);
|
||||
const { appName, envName } = props;
|
||||
return (
|
||||
const graphNode = (
|
||||
<div
|
||||
key={id}
|
||||
onClick={() => props.onNodeClick && props.onNodeClick(fullName)}
|
||||
|
@ -139,7 +156,6 @@ function renderPodNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
|||
'error-status': node.resource.healthStatus?.statusCode == 'UnHealthy',
|
||||
'warning-status': node.resource.healthStatus?.statusCode == 'Progressing',
|
||||
})}
|
||||
title={describeNode(node)}
|
||||
style={{
|
||||
left: node.x,
|
||||
top: node.y,
|
||||
|
@ -150,6 +166,7 @@ function renderPodNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
|||
>
|
||||
<div className={classNames('icon')}>
|
||||
<img src={pod} />
|
||||
<span>Pod</span>
|
||||
</div>
|
||||
<div className={classNames('name')}>
|
||||
<Link
|
||||
|
@ -172,16 +189,24 @@ function renderPodNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Balloon trigger={graphNode}>
|
||||
<div>
|
||||
{describeNode(node).map((line) => {
|
||||
return <p className="line">{line}</p>;
|
||||
})}
|
||||
</div>
|
||||
</Balloon>
|
||||
);
|
||||
}
|
||||
|
||||
function renderClusterNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
||||
const fullName = nodeKey(node);
|
||||
|
||||
return (
|
||||
const graphNode = (
|
||||
<div
|
||||
onClick={() => props.onNodeClick && props.onNodeClick(fullName)}
|
||||
className={classNames('graph-node', 'graph-node-cluster')}
|
||||
title={describeCluster(node)}
|
||||
style={{
|
||||
left: node.x,
|
||||
top: node.y,
|
||||
|
@ -194,10 +219,20 @@ function renderClusterNode(props: TreeGraphProps, id: string, node: GraphNode) {
|
|||
<img src={kubernetes} />
|
||||
</div>
|
||||
<div className={classNames('name')}>
|
||||
<span>{node.resource.name}</span>
|
||||
<div>{node.resource.name}</div>
|
||||
<div className="kind">Cluster</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Balloon trigger={graphNode}>
|
||||
<div>
|
||||
{describeCluster(node).map((line) => {
|
||||
return <p className="line">{line}</p>;
|
||||
})}
|
||||
</div>
|
||||
</Balloon>
|
||||
);
|
||||
}
|
||||
|
||||
function setNode(graph: dagre.graphlib.Graph<GraphNode, GraphEdge>, node: TreeNode) {
|
||||
|
|
|
@ -50,7 +50,7 @@ export function describeNode(node: GraphNode) {
|
|||
if (node.resource.healthStatus?.reason) {
|
||||
lines.push(`Reason: ${node.resource.healthStatus?.reason}`);
|
||||
}
|
||||
if (node.resource.kind === 'Service') {
|
||||
if (node.resource.kind === 'Service' && node.resource.additionalInfo?.EIP) {
|
||||
lines.push(`EIP: ${node.resource.additionalInfo?.EIP}`);
|
||||
}
|
||||
if (node.resource.kind === 'Pod') {
|
||||
|
@ -58,12 +58,12 @@ export function describeNode(node: GraphNode) {
|
|||
lines.push(`Ready: ${node.resource.additionalInfo?.Ready}`);
|
||||
lines.push(`Restarts: ${node.resource.additionalInfo?.Restarts}`);
|
||||
}
|
||||
return lines.join('\n');
|
||||
return lines;
|
||||
}
|
||||
|
||||
export function describeCluster(node: GraphNode) {
|
||||
const lines = [`Cluster: ${node.resource.name}`];
|
||||
return lines.join('\n');
|
||||
return lines;
|
||||
}
|
||||
|
||||
export function treeNodeKey(node: TreeNode & { uid?: string }) {
|
||||
|
@ -160,6 +160,7 @@ export const ResourceIcon = ({
|
|||
const style: React.CSSProperties = {
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
overflow: 'hidden',
|
||||
padding: `${n <= 2 ? 2 : 0}px 4px`,
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
|
|
|
@ -25,6 +25,10 @@ import i18n from 'i18next';
|
|||
import { getValue } from '../../utils/utils';
|
||||
import HelmRepoSelect from '../../extends/HelmRepoSelect';
|
||||
import PolicySelect from '../../extends/PolicySelect';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import DefinitionCode from '../DefinitionCode';
|
||||
import * as yaml from 'js-yaml';
|
||||
import type { Definition } from '../../interface/addon';
|
||||
|
||||
const { Col, Row } = Grid;
|
||||
|
||||
|
@ -32,6 +36,7 @@ type Props = {
|
|||
inline?: boolean;
|
||||
id?: string;
|
||||
value?: any;
|
||||
enableCodeEdit?: boolean;
|
||||
uiSchema?: UIParam[];
|
||||
maxColSpan?: number;
|
||||
onChange?: (params: any) => void;
|
||||
|
@ -39,6 +44,7 @@ type Props = {
|
|||
disableRenderRow?: boolean;
|
||||
mode: 'new' | 'edit';
|
||||
advanced?: boolean;
|
||||
definition?: Definition;
|
||||
};
|
||||
|
||||
function convertRule(validate?: UIParamValidate) {
|
||||
|
@ -88,6 +94,8 @@ function convertRule(validate?: UIParamValidate) {
|
|||
type State = {
|
||||
secretKeys?: string[];
|
||||
advanced: boolean;
|
||||
codeError?: string;
|
||||
codeID: string;
|
||||
};
|
||||
|
||||
class UISchema extends Component<Props, State> {
|
||||
|
@ -118,6 +126,7 @@ class UISchema extends Component<Props, State> {
|
|||
this.state = {
|
||||
secretKeys: [],
|
||||
advanced: props.advanced || false,
|
||||
codeID: uuid(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -145,11 +154,16 @@ class UISchema extends Component<Props, State> {
|
|||
|
||||
validate = (callback: (error?: string) => void) => {
|
||||
this.form.validate((errors) => {
|
||||
const { codeError } = this.state;
|
||||
if (errors) {
|
||||
console.log(errors);
|
||||
callback('ui schema validate failure');
|
||||
return;
|
||||
}
|
||||
if (codeError) {
|
||||
callback('ui schema validate failure');
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
@ -200,11 +214,74 @@ class UISchema extends Component<Props, State> {
|
|||
return false;
|
||||
};
|
||||
|
||||
renderDocumentURL = () => {
|
||||
const { definition } = this.props;
|
||||
if (definition) {
|
||||
switch (definition.type) {
|
||||
case 'component':
|
||||
return 'https://kubevela.net/docs/end-user/components/references#' + definition.name;
|
||||
case 'trait':
|
||||
return 'https://kubevela.net/docs/end-user/traits/references#' + definition.name;
|
||||
case 'policy':
|
||||
return 'https://kubevela.net/docs/end-user/policies/references#' + definition.name;
|
||||
case 'workflowstep':
|
||||
return (
|
||||
'https://kubevela.net/docs/end-user/workflow/built-in-workflow-defs#' + definition.name
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderCodeEdit = () => {
|
||||
const { value, onChange, definition } = this.props;
|
||||
const { codeError, codeID } = this.state;
|
||||
let yamlValue = yaml.dump(value);
|
||||
if (yamlValue == '{}\n') {
|
||||
yamlValue = '';
|
||||
}
|
||||
return (
|
||||
<div style={{ width: '100%' }}>
|
||||
<If condition={codeError}>
|
||||
<span style={{ color: 'red' }}>{codeError}</span>
|
||||
</If>
|
||||
<If condition={definition}>
|
||||
<p>
|
||||
Refer to the document:
|
||||
<a style={{ marginLeft: '8px' }} target="_blank" href={this.renderDocumentURL()}>
|
||||
click here
|
||||
</a>
|
||||
</p>
|
||||
</If>
|
||||
<div id={codeID} className="guide-code">
|
||||
<DefinitionCode
|
||||
value={yamlValue}
|
||||
onBlurEditor={(v) => {
|
||||
if (onChange) {
|
||||
try {
|
||||
const valueObj = yaml.load(v);
|
||||
onChange(valueObj);
|
||||
this.setState({ codeError: '' });
|
||||
} catch (err) {
|
||||
this.setState({ codeError: 'Please input a valid yaml config:' + err });
|
||||
}
|
||||
}
|
||||
}}
|
||||
id={codeID + '-code'}
|
||||
containerId={codeID}
|
||||
language={'yaml'}
|
||||
readOnly={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { advanced } = this.state;
|
||||
const { uiSchema, inline, maxColSpan, disableRenderRow, value, mode } = this.props;
|
||||
if (!uiSchema) {
|
||||
return <div />;
|
||||
const { uiSchema, inline, maxColSpan, disableRenderRow, value, mode, enableCodeEdit } =
|
||||
this.props;
|
||||
if (!uiSchema || enableCodeEdit) {
|
||||
return this.renderCodeEdit();
|
||||
}
|
||||
if (mode == 'edit' && value === undefined) {
|
||||
return <div />;
|
||||
|
@ -405,27 +482,31 @@ class UISchema extends Component<Props, State> {
|
|||
</Form.Item>
|
||||
);
|
||||
case 'ImageInput':
|
||||
const imagePullSecretsValue = value && value.imagePullSecrets;
|
||||
const initResult = init('imagePullSecrets', {
|
||||
initValue: imagePullSecretsValue,
|
||||
rules: [],
|
||||
});
|
||||
return (
|
||||
<Form.Item
|
||||
required={required}
|
||||
<ImageInput
|
||||
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>
|
||||
required={required || false}
|
||||
disabled={disableEdit}
|
||||
{...init(param.jsonKey, {
|
||||
initValue: initValue,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: checkImageName,
|
||||
message: 'Please enter a valid image name',
|
||||
},
|
||||
],
|
||||
})}
|
||||
secretValue={initResult.value}
|
||||
onSecretChange={initResult.onChange}
|
||||
secretID={initResult.id}
|
||||
/>
|
||||
);
|
||||
case 'HelmChartSelect':
|
||||
return (
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Loading, Select } from '@b-design/ui';
|
|||
import i18n from '../../i18n';
|
||||
import locale from '../../utils/locale';
|
||||
import { connect } from 'dva';
|
||||
import type { HelmRepo } from '../../interface/application';
|
||||
import type { HelmRepo } from '../../interface/repository';
|
||||
|
||||
type Props = {
|
||||
value?: any;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import { getChartVersions } from '../../api/repository';
|
||||
import { Loading, Select } from '@b-design/ui';
|
||||
import type { ChartVersion, HelmRepo } from '../../interface/application';
|
||||
import type { ChartVersion, HelmRepo } from '../../interface/repository';
|
||||
import i18n from '../../i18n';
|
||||
import locale from '../../utils/locale';
|
||||
import { connect } from 'dva';
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Loading, Select } from '@b-design/ui';
|
|||
import i18n from '../../i18n';
|
||||
import locale from '../../utils/locale';
|
||||
import { getChartRepos } from '../../api/repository';
|
||||
import type { HelmRepo } from '../../interface/application';
|
||||
import type { HelmRepo } from '../../interface/repository';
|
||||
import { connect } from 'dva';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, { Component } from 'react';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import _ from 'lodash';
|
||||
import type { HelmRepo, UIParam } from '../../interface/application';
|
||||
import type { UIParam } from '../../interface/application';
|
||||
import type { HelmRepo } from '../../interface/repository';
|
||||
import KV from '../KV';
|
||||
import { getChartValues } from '../../api/repository';
|
||||
import { Loading } from '@b-design/ui';
|
||||
|
|
|
@ -1,3 +1,51 @@
|
|||
@import '../../lib.less';
|
||||
|
||||
.image-input-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.image-info {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
padding: 8px;
|
||||
overflow-x: hidden;
|
||||
background: rgba(171, 205, 239, 0.2);
|
||||
.container-base {
|
||||
display: flex;
|
||||
padding: 8px;
|
||||
.docker-base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.docker-logo {
|
||||
width: 50px;
|
||||
margin: 0 16px 0 0;
|
||||
}
|
||||
.name {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
.desc {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
.desc {
|
||||
margin-top: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.image-item {
|
||||
display: flex;
|
||||
padding: 8px;
|
||||
svg {
|
||||
margin-right: 6px;
|
||||
font-size: 24px;
|
||||
line-height: 60px;
|
||||
}
|
||||
}
|
||||
.message {
|
||||
color: @dangerColor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,200 @@
|
|||
import React from 'react';
|
||||
import { Input } from '@b-design/ui';
|
||||
import { Input, Form, Loading, Grid, Tag } from '@b-design/ui';
|
||||
import './index.less';
|
||||
|
||||
import type { InputProps } from '@alifd/next/types/input';
|
||||
import { getImageInfo } from '../../api/repository';
|
||||
import { connect } from 'dva';
|
||||
import { Link } from 'dva/router';
|
||||
import type { ImageInfo } from '../../interface/repository';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import { AiOutlineHdd, AiOutlineExport } from 'react-icons/ai';
|
||||
import { TbBrandDocker } from 'react-icons/tb';
|
||||
import dockerLogo from '../../assets/docker.svg';
|
||||
import { beautifyTime, beautifyBinarySize } from '../../utils/common';
|
||||
import ImageSecretSelect from '../ImageSecretSelect';
|
||||
const { Col, Row } = Grid;
|
||||
|
||||
interface Props extends InputProps {
|
||||
value: any;
|
||||
key: string;
|
||||
id: string;
|
||||
label: string;
|
||||
required: boolean;
|
||||
onChange: (value: any) => void;
|
||||
disabled: boolean;
|
||||
project?: string;
|
||||
secretValue?: any;
|
||||
onSecretChange: (value?: string[]) => void;
|
||||
secretID: string;
|
||||
}
|
||||
|
||||
type State = {};
|
||||
type State = {
|
||||
imageInfo?: ImageInfo;
|
||||
imageName: string;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
@connect((store: any) => {
|
||||
return { ...store.uischema };
|
||||
})
|
||||
class ImageInput extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dataSource: [],
|
||||
imageName: '',
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount = async () => {};
|
||||
|
||||
loadImageInfo = () => {
|
||||
const { project, onChange, onSecretChange } = this.props;
|
||||
const { imageName } = this.state;
|
||||
if (project && imageName) {
|
||||
onChange(imageName);
|
||||
this.setState({ loading: true });
|
||||
getImageInfo({ project: project, name: imageName })
|
||||
.then((res: ImageInfo) => {
|
||||
if (res) {
|
||||
this.setState({ imageInfo: res });
|
||||
if (res.secretNames) {
|
||||
onSecretChange(res.secretNames);
|
||||
} else {
|
||||
onSecretChange(undefined);
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value } = this.props;
|
||||
return <Input {...this.props} defaultValue={value} />;
|
||||
const { value, id, required, label, key, onSecretChange, secretValue, disabled, secretID } =
|
||||
this.props;
|
||||
const { loading, imageInfo } = this.state;
|
||||
if (!this.state.imageName && value) {
|
||||
this.setState({ imageName: value }, () => {
|
||||
this.loadImageInfo();
|
||||
});
|
||||
}
|
||||
let secrets = secretValue;
|
||||
let secretDisabled = disabled;
|
||||
if (imageInfo && imageInfo.secretNames) {
|
||||
secrets = imageInfo.secretNames;
|
||||
secretDisabled = true;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col span={16} style={{ padding: '0 16px 0 0' }}>
|
||||
<Form.Item
|
||||
required={required}
|
||||
label={label}
|
||||
key={key}
|
||||
help={
|
||||
<span>
|
||||
To deploy from a private registry, you need to{' '}
|
||||
<Link to="/integrations/config-image-registry/config">
|
||||
create a integration configure
|
||||
</Link>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Input
|
||||
id={id}
|
||||
onChange={(name: string) => {
|
||||
this.setState({ imageName: name });
|
||||
}}
|
||||
disabled={disabled}
|
||||
defaultValue={value}
|
||||
onBlur={this.loadImageInfo}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label={'Secret'}>
|
||||
<ImageSecretSelect
|
||||
id={secretID}
|
||||
disabled={secretDisabled}
|
||||
onChange={onSecretChange}
|
||||
value={secrets}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Loading visible={loading} style={{ width: '100%' }}>
|
||||
<If condition={imageInfo}>
|
||||
<div className="image-info">
|
||||
<If condition={imageInfo?.info}>
|
||||
<Row>
|
||||
<Col xl={24} className="container-base">
|
||||
<img className="docker-logo" src={dockerLogo} />
|
||||
<div className="docker-base">
|
||||
<div>
|
||||
<span className="name">{imageInfo?.name}</span>
|
||||
</div>
|
||||
<div className="desc">
|
||||
<span title={imageInfo?.info?.created}>
|
||||
{beautifyTime(imageInfo?.info?.created)}
|
||||
</span>
|
||||
<span style={{ marginLeft: '8px' }}>
|
||||
{beautifyBinarySize(imageInfo?.size || 0)}
|
||||
</span>
|
||||
<If condition={imageInfo?.info?.architecture}>
|
||||
<span style={{ marginLeft: '8px' }}>{imageInfo?.info?.architecture}</span>
|
||||
</If>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className="image-item" xl={8}>
|
||||
<div className="image-item-icon">
|
||||
<AiOutlineHdd title="Volume" />
|
||||
</div>
|
||||
<div className="name">
|
||||
{imageInfo?.info?.config?.Volumes
|
||||
? Object.keys(imageInfo?.info?.config?.Volumes).map((path) => {
|
||||
return <Tag color="green">{path}</Tag>;
|
||||
})
|
||||
: 'No default Volume config'}
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="image-item" xl={8}>
|
||||
<div className="image-item-icon">
|
||||
<AiOutlineExport title="ExposedPorts" />
|
||||
</div>
|
||||
<div className="name">
|
||||
{imageInfo?.info?.config?.ExposedPorts
|
||||
? Object.keys(imageInfo?.info?.config?.ExposedPorts).map((port) => {
|
||||
return <Tag color="blue">{port}</Tag>;
|
||||
})
|
||||
: 'No default Port config'}
|
||||
</div>
|
||||
</Col>
|
||||
<Col className="image-item" xl={8}>
|
||||
<div className="image-item-icon">
|
||||
<TbBrandDocker />
|
||||
</div>
|
||||
<div className="name">
|
||||
<Tag title={imageInfo?.registry}>{imageInfo?.registry}</Tag>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</If>
|
||||
<If condition={imageInfo?.message}>
|
||||
<div className="message">{imageInfo?.message}</div>
|
||||
</If>
|
||||
</div>
|
||||
</If>
|
||||
</Loading>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Loading, Select } from '@b-design/ui';
|
||||
import i18n from '../../i18n';
|
||||
import locale from '../../utils/locale';
|
||||
import { getImageRepos } from '../../api/repository';
|
||||
import type { ImageRegistry } from '../../interface/repository';
|
||||
import { connect } from 'dva';
|
||||
|
||||
type Props = {
|
||||
value?: any;
|
||||
onChange: (value: any) => void;
|
||||
id: string;
|
||||
disabled: boolean;
|
||||
dispatch?: ({}) => {};
|
||||
project?: string;
|
||||
imageSecret?: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
registries: ImageRegistry[];
|
||||
inputRepo: string;
|
||||
loading: boolean;
|
||||
values?: string[];
|
||||
};
|
||||
@connect((store: any) => {
|
||||
return { ...store.uischema };
|
||||
})
|
||||
class ImageSecretSelect extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
loading: false,
|
||||
registries: [],
|
||||
inputRepo: '',
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
this.onLoadRepos();
|
||||
}
|
||||
|
||||
onLoadRepos = () => {
|
||||
const { project } = this.props;
|
||||
if (project) {
|
||||
this.setState({ loading: true });
|
||||
getImageRepos({ project: project })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
this.setState({
|
||||
registries: res.registries,
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onSearch = (value: string) => {
|
||||
this.setState({ inputRepo: value });
|
||||
};
|
||||
|
||||
convertImageRegistryOptions(data: ImageRegistry[]): { label: string; value: string }[] {
|
||||
return (data || []).map((item: ImageRegistry) => {
|
||||
let label = item.secretName;
|
||||
if (item.domain) {
|
||||
label = `${item.secretName}(${item.domain})`;
|
||||
}
|
||||
return { label: label, value: item.secretName };
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { disabled, onChange, value } = this.props;
|
||||
const { registries, loading, inputRepo } = this.state;
|
||||
const dataSource = registries;
|
||||
if (inputRepo) {
|
||||
dataSource.unshift({ secretName: inputRepo, name: inputRepo });
|
||||
}
|
||||
const transDataSource = this.convertImageRegistryOptions(dataSource);
|
||||
return (
|
||||
<Loading visible={loading} style={{ width: '100%' }}>
|
||||
<Select
|
||||
placeholder={i18n.t('Please select or input your owner image registry secret')}
|
||||
inputMode="url"
|
||||
mode="multiple"
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
showSearch={true}
|
||||
onSearch={this.onSearch}
|
||||
followTrigger={true}
|
||||
value={value}
|
||||
dataSource={transDataSource}
|
||||
locale={locale().Select}
|
||||
/>
|
||||
</Loading>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImageSecretSelect;
|
|
@ -4,7 +4,9 @@ import { Upload, Button, Icon, Message } from '@b-design/ui';
|
|||
import DefinitionCode from '../../components/DefinitionCode';
|
||||
import Translation from '../../components/Translation';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import type { KubernetesObject } from './objects';
|
||||
|
||||
type Props = {
|
||||
value: any;
|
||||
|
@ -15,6 +17,7 @@ type Props = {
|
|||
type State = {
|
||||
message: string;
|
||||
containerId: string;
|
||||
showButton: boolean;
|
||||
};
|
||||
|
||||
class K8sObjectsCode extends React.Component<Props, State> {
|
||||
|
@ -23,7 +26,8 @@ class K8sObjectsCode extends React.Component<Props, State> {
|
|||
super(props);
|
||||
this.state = {
|
||||
message: '',
|
||||
containerId: Date.now().toString(),
|
||||
containerId: uuid(),
|
||||
showButton: false,
|
||||
};
|
||||
this.form = new Field(this, {
|
||||
onChange: () => {
|
||||
|
@ -37,17 +41,20 @@ class K8sObjectsCode extends React.Component<Props, State> {
|
|||
const { value } = this.props;
|
||||
this.setValues(value);
|
||||
};
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
const { value } = nextProps;
|
||||
if (value !== this.props.value) {
|
||||
this.setValues(value);
|
||||
}
|
||||
}
|
||||
|
||||
setValues = (value: any) => {
|
||||
setValues = (value: KubernetesObject[]) => {
|
||||
if (value) {
|
||||
try {
|
||||
const code = yaml.dump(value);
|
||||
let code = '---\n';
|
||||
if (value instanceof Array) {
|
||||
value.map((res) => {
|
||||
if (res) {
|
||||
code = code + yaml.dump(res) + '---\n';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
code = yaml.dump(value) + '---\n';
|
||||
}
|
||||
this.form.setValues({ code: code });
|
||||
} catch {}
|
||||
}
|
||||
|
@ -57,10 +64,11 @@ class K8sObjectsCode extends React.Component<Props, State> {
|
|||
const { onChange, value } = this.props;
|
||||
if (onChange) {
|
||||
try {
|
||||
let object = yaml.load(v);
|
||||
let object: any = yaml.load(v);
|
||||
if (!(object instanceof Array)) {
|
||||
object = [object];
|
||||
}
|
||||
object = object.filter((ob: any) => ob != null);
|
||||
if (yaml.dump(value) != v) {
|
||||
onChange(object);
|
||||
}
|
||||
|
@ -68,11 +76,14 @@ class K8sObjectsCode extends React.Component<Props, State> {
|
|||
} catch (error: any) {
|
||||
if ((error.message = 'expected a single document in the stream, but found more')) {
|
||||
try {
|
||||
const objects = yaml.loadAll(v);
|
||||
let objects = yaml.loadAll(v);
|
||||
if (yaml.dump(value) != v) {
|
||||
objects = objects.filter((ob: any) => ob != null);
|
||||
onChange(objects);
|
||||
}
|
||||
this.setState({ message: '' });
|
||||
this.setState({
|
||||
message: '',
|
||||
});
|
||||
} catch (err: any) {
|
||||
this.setState({ message: err.message });
|
||||
}
|
||||
|
@ -96,15 +107,25 @@ class K8sObjectsCode extends React.Component<Props, State> {
|
|||
};
|
||||
};
|
||||
|
||||
onConvert2WebService = () => {};
|
||||
|
||||
render() {
|
||||
const { id } = this.props;
|
||||
const { init } = this.form;
|
||||
const { message, containerId } = this.state;
|
||||
const { message, containerId, showButton } = this.state;
|
||||
return (
|
||||
<div id={id}>
|
||||
<If condition={message}>
|
||||
<span style={{ color: 'red' }}>{message}</span>
|
||||
</If>
|
||||
|
||||
<Message type="notice" style={{ marginTop: '16px' }}>
|
||||
<Translation>
|
||||
The input data will be automatically formatted. Ensure that the input data is a valid
|
||||
k8s resource YAML.
|
||||
</Translation>
|
||||
</Message>
|
||||
|
||||
<Upload request={this.customRequest}>
|
||||
<Button text type="normal" className="padding-left-0">
|
||||
<Icon type="cloudupload" />
|
||||
|
@ -120,12 +141,19 @@ class K8sObjectsCode extends React.Component<Props, State> {
|
|||
{...init('code')}
|
||||
/>
|
||||
</div>
|
||||
<Message type="notice" style={{ marginTop: '16px' }}>
|
||||
<Translation>
|
||||
The input data will be automatically formatted. Ensure that the input data is a valid
|
||||
k8s resource YAML.
|
||||
</Translation>
|
||||
</Message>
|
||||
|
||||
<If condition={showButton}>
|
||||
<div style={{ marginTop: '16px' }}>
|
||||
<span style={{ fontSize: '14px', color: '#000', marginRight: '16px' }}>
|
||||
<Translation>
|
||||
Convert the kubernetes resource component to the webservice component?
|
||||
</Translation>
|
||||
</span>
|
||||
<Button type="secondary" onClick={this.onConvert2WebService}>
|
||||
Yes
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import type { ResourceObject } from '../../interface/observation';
|
||||
|
||||
export interface KubernetesObject extends ResourceObject {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
spec?: Record<string, any>;
|
||||
}
|
||||
|
||||
export function isDeployment(object: KubernetesObject): boolean {
|
||||
return object.kind === 'Deployment';
|
||||
}
|
||||
|
||||
export function isShowConvertButton(objects: KubernetesObject[]): boolean {
|
||||
return objects.filter((ob) => isDeployment(ob)).length > 0;
|
||||
}
|
||||
|
||||
export function buildWebServiceBaseDeployment(object: KubernetesObject): Record<string, any> {
|
||||
return {
|
||||
image: object.spec?.containers[0].image,
|
||||
ports: [],
|
||||
readinessProbe: {},
|
||||
livenessProbe: {},
|
||||
env: [],
|
||||
memory: '',
|
||||
cpu: '',
|
||||
};
|
||||
}
|
|
@ -42,9 +42,12 @@ class PolicySelect extends React.Component<Props, State> {
|
|||
})
|
||||
.then((res) => {
|
||||
if (res && res.policies) {
|
||||
const policyListData = (res.policies || []).map(
|
||||
(item: ApplicationPolicyBase) => `${item.name}(${item.type})`,
|
||||
);
|
||||
const policyListData = (res.policies || []).map((item: ApplicationPolicyBase) => {
|
||||
return {
|
||||
label: `${item.name}(${item.type})`,
|
||||
value: item.name,
|
||||
};
|
||||
});
|
||||
this.setState({
|
||||
policySelectDataSource: policyListData,
|
||||
});
|
||||
|
|
|
@ -41,7 +41,11 @@ export interface ApplicationBase {
|
|||
}
|
||||
|
||||
export interface DefinitionDetail {
|
||||
name: string;
|
||||
description: string;
|
||||
uiSchema: UIParam[];
|
||||
labels: Record<string, string>;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface UIParam {
|
||||
|
@ -86,20 +90,6 @@ export interface UIParamValidate {
|
|||
immutable?: boolean;
|
||||
}
|
||||
|
||||
export interface ImageInfo {
|
||||
imageURL: string;
|
||||
imageName: string;
|
||||
tag: string;
|
||||
repoHost: string;
|
||||
namespace: string;
|
||||
ports: number[];
|
||||
volumes: ImageVolume[];
|
||||
}
|
||||
|
||||
export interface ImageVolume {
|
||||
mountPath: string;
|
||||
}
|
||||
|
||||
export interface ApplicationDeployRequest {
|
||||
appName: string;
|
||||
workflowName?: string;
|
||||
|
@ -324,26 +314,6 @@ export interface TraitDefinitionSpec {
|
|||
podDisruptive?: boolean;
|
||||
appliesToWorkloads?: string[];
|
||||
}
|
||||
|
||||
export interface ChartVersion {
|
||||
name: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
apiVersion: string;
|
||||
appVersion: string;
|
||||
type?: string;
|
||||
urls: string[];
|
||||
created: string;
|
||||
digest: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface HelmRepo {
|
||||
url: string;
|
||||
type: string;
|
||||
secretName?: string;
|
||||
}
|
||||
|
||||
export interface ApplicationQuery {
|
||||
query?: string;
|
||||
project?: string;
|
||||
|
|
|
@ -93,6 +93,8 @@ export interface Metadata {
|
|||
resourceVersion: string;
|
||||
selfLink: string;
|
||||
uid: string;
|
||||
annotations?: Record<string, string>;
|
||||
labels?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ManagedFields {
|
||||
|
@ -111,28 +113,22 @@ export interface CloudResource {
|
|||
status: string;
|
||||
}
|
||||
|
||||
export interface Configuration {
|
||||
export interface Configuration extends ResourceObject {
|
||||
apiVersion: string;
|
||||
kind: string;
|
||||
metadata: {
|
||||
name: string;
|
||||
namespace: string;
|
||||
creationTimestamp: string;
|
||||
annotations: any;
|
||||
labels: any;
|
||||
};
|
||||
spec: {
|
||||
region: string;
|
||||
providerRef: {
|
||||
name: string;
|
||||
namespace: string;
|
||||
};
|
||||
region?: string;
|
||||
};
|
||||
status?: {
|
||||
apply?: {
|
||||
outputs?: any;
|
||||
state?: string;
|
||||
message?: string;
|
||||
region?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
export interface HelmRepo {
|
||||
url: string;
|
||||
type: string;
|
||||
secretName?: string;
|
||||
}
|
||||
|
||||
export interface ChartVersion {
|
||||
name: string;
|
||||
version: string;
|
||||
description?: string;
|
||||
apiVersion: string;
|
||||
appVersion: string;
|
||||
type?: string;
|
||||
urls: string[];
|
||||
created: string;
|
||||
digest: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface ImageInfo {
|
||||
info?: {
|
||||
architecture: string;
|
||||
config?: {
|
||||
ArgsEscaped: boolean;
|
||||
Cmd: string[];
|
||||
Env: string[];
|
||||
Labels: Record<string, string>;
|
||||
Volumes: Record<string, any>;
|
||||
ExposedPorts: Record<string, any>;
|
||||
};
|
||||
manifest?: {
|
||||
config: {
|
||||
size: number;
|
||||
mediaType: string;
|
||||
};
|
||||
layers: {
|
||||
mediaType: string;
|
||||
size: number;
|
||||
}[];
|
||||
};
|
||||
created: string;
|
||||
os: string;
|
||||
docker_version: string;
|
||||
author?: string;
|
||||
};
|
||||
size: number;
|
||||
name: string;
|
||||
secretNames?: string[];
|
||||
registry: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface ImageRegistry {
|
||||
name?: string;
|
||||
secretName: string;
|
||||
domain?: string;
|
||||
}
|
|
@ -50,6 +50,13 @@ class TabsContent extends Component<Props, State> {
|
|||
payload: { appName: appName },
|
||||
});
|
||||
};
|
||||
loadApplicationPolicies = async () => {
|
||||
const { appName } = this.props;
|
||||
this.props.dispatch({
|
||||
type: 'application/getApplicationPolicies',
|
||||
payload: { appName: appName },
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const { activeKey, applicationDetail, envbinding } = this.props;
|
||||
const { visibleEnvPlan } = this.state;
|
||||
|
@ -128,6 +135,7 @@ class TabsContent extends Component<Props, State> {
|
|||
onOK={() => {
|
||||
this.loadEnvbinding();
|
||||
this.loadApplicationWorkflows();
|
||||
this.loadApplicationPolicies();
|
||||
this.setState({ visibleEnvPlan: false });
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -65,6 +65,8 @@ const LeftMenu = (props: Props) => {
|
|||
return null;
|
||||
});
|
||||
|
||||
const releaseVersion = process.env.VERSION || version;
|
||||
|
||||
return (
|
||||
<div style={{ position: 'relative', height: '100%' }}>
|
||||
<div className="slide-wrapper">
|
||||
|
@ -74,7 +76,7 @@ const LeftMenu = (props: Props) => {
|
|||
align="t"
|
||||
trigger={
|
||||
<div className="nav-container">
|
||||
{version}-{__COMMIT_HASH__}
|
||||
{releaseVersion}-{__COMMIT_HASH__}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
|
@ -75,7 +75,7 @@ export function getLeftSlider(pathname) {
|
|||
link: '/definitions',
|
||||
iconType: 'database-set',
|
||||
navName: 'Definitions',
|
||||
permission: { resource: 'definition:*', action: 'update' },
|
||||
permission: { resource: 'definition:*', action: 'list' },
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -77,8 +77,8 @@
|
|||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
@media (max-width: 980px) {
|
||||
.layout-navigation {
|
||||
min-width: 180px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
@primarycolor: #1b58f4;
|
||||
@dangerColor: rgb(248, 81, 73);
|
||||
@warningColor: var(--message-warning-color-icon-inline,#fac800);
|
||||
@warningColor: var(--message-warning-color-icon-inline, #fac800);
|
||||
@colorRED: red;
|
||||
@colorGREEN: green;
|
||||
@colorBLUE: blue;
|
||||
|
@ -78,4 +78,4 @@
|
|||
@font-size-14: 14px;
|
||||
@font-size-16: 16px;
|
||||
@font-size-18: 18px;
|
||||
@font-size-20: 20px;
|
||||
@font-size-20: 20px;
|
||||
|
|
|
@ -552,5 +552,7 @@
|
|||
"Your component type does not support the image field, and the image update cannot be performed": "选择的组件不存在 image 字段,无法进行镜像更新",
|
||||
"Please select a component": "请选择一个组件",
|
||||
"Please select a workflow": "请选择一个工作流",
|
||||
"Please select a payloadType": "请选择请求体类型,不同的外部系统请求参数类型具有差异"
|
||||
"Please select a payloadType": "请选择请求体类型,不同的外部系统请求参数类型具有差异",
|
||||
"Resource Graph": "资源拓扑图",
|
||||
"The ImagePullSecrets property has been automatically assigned": "发现镜像仓库配置,已自动为 ImagePullSecrets 字段赋值"
|
||||
}
|
|
@ -54,6 +54,17 @@ class CardContent extends React.Component<Props, State> {
|
|||
return '';
|
||||
}
|
||||
};
|
||||
const nameUpper = (name: string) => {
|
||||
return name
|
||||
.split('-')
|
||||
.map((sep) => {
|
||||
if (sep.length > 0) {
|
||||
return sep.toUpperCase()[0];
|
||||
}
|
||||
})
|
||||
.toString()
|
||||
.replace(',', '');
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<If condition={addonLists}>
|
||||
|
@ -72,7 +83,23 @@ class CardContent extends React.Component<Props, State> {
|
|||
<img src={icon} />
|
||||
</If>
|
||||
<If condition={!icon || icon === 'none'}>
|
||||
<img />
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
padding: `2px 4px`,
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: '#fff',
|
||||
textAlign: 'center',
|
||||
lineHeight: '60px',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#1b58f4', fontSize: `2em` }}>
|
||||
{nameUpper(name)}
|
||||
</span>
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
</a>
|
||||
|
|
|
@ -7,3 +7,19 @@
|
|||
width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
.addon-readme {
|
||||
font-size: 14px !important;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: decimal;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.addon-readme li>p {
|
||||
font-size: 14px;
|
||||
}
|
|
@ -22,7 +22,6 @@ import {
|
|||
} from '../../../../api/addons';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import './index.less';
|
||||
import Empty from '../../../../components/Empty';
|
||||
import DrawerWithFooter from '../../../../components/Drawer';
|
||||
import Group from '../../../../extends/Group';
|
||||
|
@ -35,6 +34,8 @@ import type { ApplicationStatus } from '../../../../interface/application';
|
|||
import i18n from '../../../../i18n';
|
||||
import type { NameAlias } from '../../../../interface/env';
|
||||
import Permission from '../../../../components/Permission';
|
||||
import 'github-markdown-css/github-markdown-light.css';
|
||||
import './index.less';
|
||||
|
||||
type Props = {
|
||||
addonName: string;
|
||||
|
@ -515,11 +516,12 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
<Card
|
||||
contentHeight="auto"
|
||||
locale={locale().Card}
|
||||
title={<Translation>Readme</Translation>}
|
||||
title={<Translation>README</Translation>}
|
||||
style={{ marginTop: '16px' }}
|
||||
>
|
||||
<If condition={addonDetailInfo?.detail}>
|
||||
<ReactMarkdown
|
||||
className="markdown-body addon-readme"
|
||||
children={addonDetailInfo?.detail || ''}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Grid, Field, Form, Message, Button, Input, Select, Divider } from '@b-design/ui';
|
||||
import { Grid, Field, Form, Message, Button, Input, Select, Divider, Icon } from '@b-design/ui';
|
||||
import type { Rule } from '@alifd/field';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import {
|
||||
|
@ -50,6 +50,7 @@ type State = {
|
|||
isUpdateComponentLoading: boolean;
|
||||
editComponent?: ApplicationComponent;
|
||||
loading: boolean;
|
||||
propertiesMode: string;
|
||||
};
|
||||
|
||||
@connect()
|
||||
|
@ -63,6 +64,7 @@ class ComponentDialog extends React.Component<Props, State> {
|
|||
isCreateComponentLoading: false,
|
||||
isUpdateComponentLoading: false,
|
||||
loading: true,
|
||||
propertiesMode: 'native',
|
||||
};
|
||||
this.uiSchemaRef = React.createRef();
|
||||
}
|
||||
|
@ -336,7 +338,7 @@ class ComponentDialog extends React.Component<Props, State> {
|
|||
const FormItem = Form.Item;
|
||||
const { Row, Col } = Grid;
|
||||
const { isEditComponent, componentDefinitions, onComponentClose } = this.props;
|
||||
const { definitionDetail, loading } = this.state;
|
||||
const { definitionDetail, loading, propertiesMode } = this.state;
|
||||
const validator = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||
this.uiSchemaRef.current?.validate(callback);
|
||||
};
|
||||
|
@ -487,10 +489,43 @@ class ComponentDialog extends React.Component<Props, State> {
|
|||
</Row>
|
||||
</Group>
|
||||
<section className="title">
|
||||
<Title title={i18n.t('Deployment Properties')} actions={[]} />
|
||||
<Title
|
||||
title={i18n.t('Deployment Properties')}
|
||||
actions={
|
||||
definitionDetail && definitionDetail.uiSchema
|
||||
? [
|
||||
<Button
|
||||
style={{ marginTop: '-12px' }}
|
||||
onClick={() => {
|
||||
if (propertiesMode === 'native') {
|
||||
this.setState({ propertiesMode: 'code' });
|
||||
} else {
|
||||
this.setState({ propertiesMode: 'native' });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<If condition={propertiesMode === 'native'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'display-code'}
|
||||
title={'Switch to code mode'}
|
||||
/>
|
||||
</If>
|
||||
<If condition={propertiesMode === 'code'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'laptop'}
|
||||
title={'Switch to native mode'}
|
||||
/>
|
||||
</If>
|
||||
</Button>,
|
||||
]
|
||||
: []
|
||||
}
|
||||
/>
|
||||
</section>
|
||||
<Row>
|
||||
<If condition={definitionDetail && definitionDetail.uiSchema}>
|
||||
<If condition={definitionDetail}>
|
||||
<UISchema
|
||||
{...init(`properties`, {
|
||||
rules: [
|
||||
|
@ -500,7 +535,13 @@ class ComponentDialog extends React.Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
enableCodeEdit={propertiesMode === 'code'}
|
||||
uiSchema={definitionDetail && definitionDetail.uiSchema}
|
||||
definition={{
|
||||
name: definitionDetail?.name || '',
|
||||
type: 'component',
|
||||
description: definitionDetail?.description || '',
|
||||
}}
|
||||
ref={this.uiSchemaRef}
|
||||
mode={isEditComponent ? 'edit' : 'new'}
|
||||
/>
|
||||
|
|
|
@ -1,67 +1,65 @@
|
|||
|
||||
.cloud-component-show {
|
||||
border-top: #1b58f4 2px solid;
|
||||
}
|
||||
.components-list-nav {
|
||||
.cloud-component-show {
|
||||
border-top: #1b58f4 2px solid;
|
||||
}
|
||||
.components-list-nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
.components-list-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
.components-list-title {
|
||||
height: 24px;
|
||||
color: #1b58f4;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.components-list-operation {
|
||||
line-height: 24px;
|
||||
.next-icon:hover {
|
||||
color: #1b58f4;
|
||||
}
|
||||
}
|
||||
.components-list-content {
|
||||
display: flex;
|
||||
min-height: 48px;
|
||||
padding: 16px 0;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.components-list-date {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
height: 37px;
|
||||
line-height: 37px;
|
||||
}
|
||||
.trait-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
margin: 4px 4px 0 0;
|
||||
padding: 8px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
height: 24px;
|
||||
color: #1b58f4;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
.trait-actions {
|
||||
background: rgba(166, 166, 166, 0.9);
|
||||
display:none;
|
||||
color: #1b58f4;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 9.5px;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
}
|
||||
.trait-icon:hover {
|
||||
border: #1b58f4 solid 1px;
|
||||
.trait-actions {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.components-list-operation {
|
||||
line-height: 24px;
|
||||
.next-icon:hover {
|
||||
color: #1b58f4;
|
||||
}
|
||||
.component-icon {
|
||||
width: 20px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.components-list-content {
|
||||
display: flex;
|
||||
min-height: 48px;
|
||||
padding: 16px 0;
|
||||
color: #a6a6a6;
|
||||
}
|
||||
.components-list-date {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
height: 37px;
|
||||
line-height: 37px;
|
||||
}
|
||||
.trait-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
margin: 4px 4px 0 0;
|
||||
padding: 8px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
.trait-actions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: none;
|
||||
width: 40px;
|
||||
padding: 9.5px;
|
||||
color: #1b58f4;
|
||||
text-align: center;
|
||||
background: rgba(166, 166, 166, 0.9);
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
}
|
||||
.trait-icon:hover {
|
||||
border: #1b58f4 solid 1px;
|
||||
.trait-actions {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.component-icon {
|
||||
width: 20px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
|
|
@ -17,4 +17,3 @@
|
|||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Card, Dialog, Grid } from '@b-design/ui';
|
||||
import { Card, Dialog, Grid, Icon } from '@b-design/ui';
|
||||
import type {
|
||||
ApplicationDetail,
|
||||
ApplicationPolicyBase,
|
||||
|
@ -12,6 +12,7 @@ import Empty from '../../../../components/Empty';
|
|||
import Translation from '../../../../components/Translation';
|
||||
import locale from '../../../../utils/locale';
|
||||
import Item from '../../../../components/Item';
|
||||
import Permission from '../../../../components/Permission';
|
||||
// import Permission from '../../../../components/Permission';
|
||||
|
||||
type Props = {
|
||||
|
@ -43,13 +44,13 @@ class PolicyList extends Component<Props, State> {
|
|||
|
||||
render() {
|
||||
const { Row, Col } = Grid;
|
||||
const { policies, envbinding } = this.props;
|
||||
const { policies, envbinding, applicationDetail } = this.props;
|
||||
const envNameAlias: any = {};
|
||||
envNameAlias[''] = '-';
|
||||
envbinding?.map((item) => {
|
||||
envNameAlias[item.name] = item.alias;
|
||||
});
|
||||
//const projectName = applicationDetail && applicationDetail.project?.name;
|
||||
const projectName = applicationDetail && applicationDetail.project?.name;
|
||||
return (
|
||||
<div className="list-warper">
|
||||
<div className="box">
|
||||
|
@ -63,24 +64,24 @@ class PolicyList extends Component<Props, State> {
|
|||
{item.alias ? `${item.alias}(${item.name})` : item.name}
|
||||
{/* </a> */}
|
||||
</div>
|
||||
{/* <div className="trigger-list-operation">
|
||||
<Permission
|
||||
request={{
|
||||
resource: `project:${projectName}/application/policy:${item.name}`,
|
||||
action: 'delete',
|
||||
}}
|
||||
project={projectName}
|
||||
>
|
||||
<Icon
|
||||
type="ashbin1"
|
||||
size={14}
|
||||
className="margin-right-0 cursor-pointer"
|
||||
onClick={() => {
|
||||
this.handlePolicyDelete(item.name);
|
||||
<div className="trigger-list-operation">
|
||||
<Permission
|
||||
request={{
|
||||
resource: `project:${projectName}/application/policy:${item.name}`,
|
||||
action: 'delete',
|
||||
}}
|
||||
/>
|
||||
</Permission>
|
||||
</div> */}
|
||||
project={projectName}
|
||||
>
|
||||
<Icon
|
||||
type="ashbin1"
|
||||
size={14}
|
||||
className="margin-right-0 cursor-pointer"
|
||||
onClick={() => {
|
||||
this.handlePolicyDelete(item.name);
|
||||
}}
|
||||
/>
|
||||
</Permission>
|
||||
</div>
|
||||
</div>
|
||||
<div className="policy-list-content">
|
||||
<Row wrap={true}>
|
||||
|
|
|
@ -47,6 +47,7 @@ type State = {
|
|||
traitDefinitions?: DefinitionBase[];
|
||||
podDisruptive?: any;
|
||||
component?: ApplicationComponent;
|
||||
propertiesMode: 'native' | 'code';
|
||||
};
|
||||
@connect()
|
||||
class TraitDialog extends React.Component<Props, State> {
|
||||
|
@ -58,6 +59,7 @@ class TraitDialog extends React.Component<Props, State> {
|
|||
definitionLoading: false,
|
||||
isLoading: false,
|
||||
traitDefinitions: [],
|
||||
propertiesMode: 'native',
|
||||
};
|
||||
this.field = new Field(this);
|
||||
this.uiSchemaRef = React.createRef();
|
||||
|
@ -286,7 +288,7 @@ class TraitDialog extends React.Component<Props, State> {
|
|||
const FormItem = Form.Item;
|
||||
const { Row, Col } = Grid;
|
||||
const { onClose, isEditTrait } = this.props;
|
||||
const { definitionDetail, definitionLoading, podDisruptive } = this.state;
|
||||
const { definitionDetail, definitionLoading, podDisruptive, propertiesMode } = this.state;
|
||||
const validator = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||
this.uiSchemaRef.current?.validate(callback);
|
||||
};
|
||||
|
@ -393,8 +395,37 @@ class TraitDialog extends React.Component<Props, State> {
|
|||
required={true}
|
||||
hasToggleIcon={true}
|
||||
>
|
||||
<If condition={definitionDetail && definitionDetail.uiSchema}>
|
||||
<If condition={definitionDetail}>
|
||||
<FormItem required={true}>
|
||||
<If condition={definitionDetail && definitionDetail.uiSchema}>
|
||||
<div className="flexright">
|
||||
<Button
|
||||
style={{ marginTop: '-12px' }}
|
||||
onClick={() => {
|
||||
if (propertiesMode === 'native') {
|
||||
this.setState({ propertiesMode: 'code' });
|
||||
} else {
|
||||
this.setState({ propertiesMode: 'native' });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<If condition={propertiesMode === 'native'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'display-code'}
|
||||
title={'Switch to code mode'}
|
||||
/>
|
||||
</If>
|
||||
<If condition={propertiesMode === 'code'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'laptop'}
|
||||
title={'Switch to native mode'}
|
||||
/>
|
||||
</If>
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
<UISchema
|
||||
key={traitType}
|
||||
{...init(`properties`, {
|
||||
|
@ -405,7 +436,13 @@ class TraitDialog extends React.Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
enableCodeEdit={propertiesMode === 'code'}
|
||||
uiSchema={definitionDetail && definitionDetail.uiSchema}
|
||||
definition={{
|
||||
type: 'trait',
|
||||
name: definitionDetail?.name || '',
|
||||
description: definitionDetail?.description || '',
|
||||
}}
|
||||
ref={this.uiSchemaRef}
|
||||
mode={this.props.isEditTrait ? 'edit' : 'new'}
|
||||
/>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
.list-warper {
|
||||
padding: 0 8px;
|
||||
.box {
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
background: #fff;
|
||||
.box-item {
|
||||
margin-bottom: 4px;
|
||||
.next-card {
|
||||
|
@ -24,4 +24,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
deleteComponent,
|
||||
getComponentDefinitions,
|
||||
deleteApplicationPlan,
|
||||
deletePolicy,
|
||||
} from '../../api/application';
|
||||
import Translation from '../../components/Translation';
|
||||
import Title from '../../components/Title';
|
||||
|
@ -370,6 +371,26 @@ class ApplicationConfig extends Component<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
onDeletePolicy = (policyName: string) => {
|
||||
const { appName } = this.state;
|
||||
deletePolicy({ appName: appName, policyName: policyName }).then((re) => {
|
||||
if (re) {
|
||||
Message.success('Application policy deleted successfully');
|
||||
this.loadApplicationPolicies();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadApplicationPolicies = async () => {
|
||||
const {
|
||||
params: { appName },
|
||||
} = this.props.match;
|
||||
this.props.dispatch({
|
||||
type: 'application/getApplicationPolicies',
|
||||
payload: { appName: appName },
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { applicationDetail, workflows, components, policies, envbinding } = this.props;
|
||||
const {
|
||||
|
@ -555,7 +576,7 @@ class ApplicationConfig extends Component<Props, State> {
|
|||
policies={policies}
|
||||
envbinding={envbinding}
|
||||
onDeletePolicy={(name: string) => {
|
||||
console.log(name);
|
||||
this.onDeletePolicy(name);
|
||||
}}
|
||||
onShowPolicy={(name: string) => {
|
||||
console.log(name);
|
||||
|
|
|
@ -15,7 +15,7 @@ import type {
|
|||
} from '../../../../interface/application';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
import locale from '../../../../utils/locale';
|
||||
import { Link } from 'dva/router';
|
||||
import { Link, routerRedux } from 'dva/router';
|
||||
import i18n from 'i18next';
|
||||
import Permission from '../../../../components/Permission';
|
||||
|
||||
|
@ -113,13 +113,14 @@ class Header extends Component<Props, State> {
|
|||
Dialog.confirm({
|
||||
content: i18n.t('Are you sure you want to delete the current environment binding?'),
|
||||
onOk: () => {
|
||||
const { applicationDetail, envName, updateEnvs } = this.props;
|
||||
const { applicationDetail, envName, updateEnvs, dispatch } = this.props;
|
||||
if (applicationDetail) {
|
||||
deleteApplicationEnvbinding({ appName: applicationDetail.name, envName: envName }).then(
|
||||
(re) => {
|
||||
if (re) {
|
||||
Message.success(i18n.t('Environment binding deleted successfully'));
|
||||
updateEnvs();
|
||||
dispatch(routerRedux.push(`/applications/${applicationDetail.name}/config`));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -99,7 +99,7 @@ class PodDetail extends React.Component<Props, State> {
|
|||
this.setState({ showContainerLog: true, containerName: containerName });
|
||||
};
|
||||
|
||||
getContainerCloumns = () => {
|
||||
getContainerColumns = () => {
|
||||
const { observability } = this.state;
|
||||
const { clusterName, env, pod, application } = this.props;
|
||||
let domain = '';
|
||||
|
@ -244,7 +244,7 @@ class PodDetail extends React.Component<Props, State> {
|
|||
];
|
||||
};
|
||||
|
||||
getEventCloumns = () => {
|
||||
getEventColumns = () => {
|
||||
return [
|
||||
{
|
||||
key: 'type',
|
||||
|
@ -282,15 +282,15 @@ class PodDetail extends React.Component<Props, State> {
|
|||
|
||||
render() {
|
||||
const { Column } = Table;
|
||||
const containerColumns = this.getContainerCloumns();
|
||||
const eventCloumns = this.getEventCloumns();
|
||||
const containerColumns = this.getContainerColumns();
|
||||
const eventColumns = this.getEventColumns();
|
||||
const { events, containers, loading, showContainerLog, containerName } = this.state;
|
||||
const { pod, clusterName } = this.props;
|
||||
return (
|
||||
<div className="table-podDetails-list margin-top-20">
|
||||
<Table
|
||||
className="container-table-wraper margin-top-20"
|
||||
dataSource={containers}
|
||||
className="container-table-wrapper margin-top-20"
|
||||
dataSource={containers || []}
|
||||
hasBorder={false}
|
||||
primaryKey="name"
|
||||
loading={loading}
|
||||
|
@ -301,15 +301,15 @@ class PodDetail extends React.Component<Props, State> {
|
|||
</Table>
|
||||
|
||||
<Table
|
||||
className="event-table-wraper margin-top-20"
|
||||
dataSource={events}
|
||||
className="event-table-wrapper margin-top-20"
|
||||
dataSource={events || []}
|
||||
hasBorder={false}
|
||||
loading={loading}
|
||||
primaryKey="time"
|
||||
locale={locale().Table}
|
||||
>
|
||||
{eventCloumns &&
|
||||
eventCloumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||
{eventColumns &&
|
||||
eventColumns.map((col, key) => <Column {...col} key={key} align={'left'} />)}
|
||||
</Table>
|
||||
|
||||
<If condition={showContainerLog}>
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
.podlist-table-wraper {
|
||||
.podlist-table-wrapper {
|
||||
.next-table-cell.last .next-table-cell-wrapper {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.container-table-wraper {
|
||||
.container-table-wrapper {
|
||||
.next-table-cell.last .next-table-cell-wrapper {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.event-table-wraper {
|
||||
.event-table-wrapper {
|
||||
.next-table-cell.last .next-table-cell-wrapper {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
@ -184,10 +184,15 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
const instances: CloudInstance[] = [];
|
||||
if (Array.isArray(configurations) && configurations.length > 0) {
|
||||
configurations.map((configuration) => {
|
||||
let url = configuration.metadata.annotations['cloud-resource/console-url'];
|
||||
const identifierKey = configuration.metadata.annotations['cloud-resource/identifier'];
|
||||
let url = '';
|
||||
let identifierKey = '';
|
||||
if (configuration.metadata.annotations) {
|
||||
url = configuration.metadata.annotations['cloud-resource/console-url'];
|
||||
identifierKey = configuration.metadata.annotations['cloud-resource/identifier'];
|
||||
}
|
||||
const outputs = configuration.status?.apply?.outputs;
|
||||
let instanceName = '';
|
||||
const region = configuration.status?.apply?.region || configuration.spec.region || '';
|
||||
if (outputs) {
|
||||
if (outputs[identifierKey]) {
|
||||
instanceName = outputs[identifierKey].value;
|
||||
|
@ -197,8 +202,8 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
if (Array.isArray(params) && params.length > 0) {
|
||||
params.map((param) => {
|
||||
const paramKey = param.replace('{', '').replace('}', '');
|
||||
if (paramKey.toLowerCase() == 'region' && configuration.spec.region) {
|
||||
url = url.replace(param, configuration.spec.region);
|
||||
if (paramKey.toLowerCase() == 'region' && region) {
|
||||
url = url.replace(param, region);
|
||||
}
|
||||
if (outputs[paramKey]) {
|
||||
url = url.replace(param, outputs[paramKey].value);
|
||||
|
@ -209,12 +214,13 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
}
|
||||
instances.push({
|
||||
instanceName: instanceName,
|
||||
status: configuration.status?.apply?.state || 'Initing',
|
||||
status: configuration.status?.apply?.state || '-',
|
||||
url: url,
|
||||
createTime: configuration.metadata.creationTimestamp,
|
||||
region: configuration.spec.region,
|
||||
createTime: configuration.metadata.creationTimestamp || '',
|
||||
region: region,
|
||||
message: configuration.status?.apply?.message,
|
||||
type: configuration.metadata.labels['workload.oam.dev/type'],
|
||||
type:
|
||||
configuration.metadata.labels && configuration.metadata.labels['workload.oam.dev/type'],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -549,11 +555,11 @@ class ApplicationInstanceList extends React.Component<Props, State> {
|
|||
<Table
|
||||
style={{ marginBottom: '32px' }}
|
||||
locale={locale().Table}
|
||||
className="podlist-table-wraper"
|
||||
className="podlist-table-wrapper"
|
||||
size="medium"
|
||||
primaryKey={'primaryKey'}
|
||||
loading={loading}
|
||||
dataSource={podList}
|
||||
dataSource={podList || []}
|
||||
expandedIndexSimulate
|
||||
expandedRowRender={expandedRowRender}
|
||||
openRowKeys={this.state.openRowKeys}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
@import '../../../../lib.less';
|
||||
|
||||
.graph-container {
|
||||
padding: 32px;
|
||||
overflow: auto;
|
||||
background: rgba(171, 205, 239, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: calc(100vh - 443px);
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
padding: 32px;
|
||||
overflow: auto;
|
||||
background: rgba(171, 205, 239, 0.2);
|
||||
//position: relative;
|
||||
.operation {
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,17 @@ import React from 'react';
|
|||
import { If } from 'tsx-control-statements/components';
|
||||
import type { TreeNode } from '../../../../components/TreeGraph';
|
||||
import { TreeGraph } from '../../../../components/TreeGraph';
|
||||
import type { ApplicationDetail, EnvBinding } from '../../../../interface/application';
|
||||
import type {
|
||||
ApplicationDetail,
|
||||
ApplicationStatus,
|
||||
EnvBinding,
|
||||
} from '../../../../interface/application';
|
||||
import type { AppliedResource, ResourceTreeNode } from '../../../../interface/observation';
|
||||
import { ShowResource } from './resource-show';
|
||||
import './index.less';
|
||||
|
||||
type Props = {
|
||||
applicationStatus?: ApplicationStatus;
|
||||
application?: ApplicationDetail;
|
||||
env?: EnvBinding;
|
||||
resources: AppliedResource[];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "../../lib.less";
|
||||
@import '../../lib.less';
|
||||
|
||||
.application-status-wrapper {
|
||||
.tags-content {
|
||||
|
@ -16,12 +16,13 @@
|
|||
.next-switch {
|
||||
width: 80px;
|
||||
}
|
||||
.next-switch-on,.next-switch-off {
|
||||
.next-switch-on,
|
||||
.next-switch-off {
|
||||
background-color: (@primarycolor) !important;
|
||||
}
|
||||
.next-switch-children {
|
||||
display: block !important;
|
||||
text-align: center;
|
||||
color: #fff !important;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
Message,
|
||||
Dialog,
|
||||
Tag,
|
||||
Switch,
|
||||
Tab,
|
||||
} from '@b-design/ui';
|
||||
import type {
|
||||
ApplicationComponent,
|
||||
|
@ -42,6 +42,7 @@ import { checkPermission } from '../../utils/permission';
|
|||
import type { LoginUserInfo } from '../../interface/user';
|
||||
import './index.less';
|
||||
import ApplicationGraph from './components/ApplicationGraph';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
type Props = {
|
||||
dispatch: ({}) => {};
|
||||
|
@ -69,7 +70,7 @@ type State = {
|
|||
resourceLoading: boolean;
|
||||
endpointLoading: boolean;
|
||||
envName: string;
|
||||
mode: 'table' | 'graph';
|
||||
mode: 'overview' | 'resource-graph' | 'application-graph';
|
||||
};
|
||||
|
||||
@connect((store: any) => {
|
||||
|
@ -84,7 +85,7 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
resourceLoading: false,
|
||||
endpointLoading: false,
|
||||
envName: '',
|
||||
mode: 'graph',
|
||||
mode: 'overview',
|
||||
resources: [],
|
||||
};
|
||||
}
|
||||
|
@ -114,9 +115,11 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
this.props.dispatch({
|
||||
type: 'application/getApplicationStatus',
|
||||
payload: { appName: appName, envName: envName },
|
||||
callback: () => {
|
||||
this.loadApplicationEndpoints();
|
||||
this.loadApplicationAppliedResources();
|
||||
callback: (res: any) => {
|
||||
if (res.status) {
|
||||
this.loadApplicationEndpoints();
|
||||
this.loadApplicationAppliedResources();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -175,7 +178,7 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
|
||||
loadApplicationAppliedResources = async () => {
|
||||
const { mode } = this.state;
|
||||
if (mode == 'graph') {
|
||||
if (mode === 'resource-graph') {
|
||||
await this.loadResourceTree();
|
||||
return;
|
||||
}
|
||||
|
@ -256,6 +259,16 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
loadApplicationPolicies = async () => {
|
||||
const {
|
||||
params: { appName },
|
||||
} = this.props.match;
|
||||
this.props.dispatch({
|
||||
type: 'application/getApplicationPolicies',
|
||||
payload: { appName: appName },
|
||||
});
|
||||
};
|
||||
|
||||
loadApplicationEnvbinding = async () => {
|
||||
const {
|
||||
params: { appName },
|
||||
|
@ -325,14 +338,13 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
}
|
||||
};
|
||||
|
||||
onChangeMode = () => {
|
||||
const { mode } = this.state;
|
||||
if (mode == 'graph') {
|
||||
this.setState({ mode: 'table' }, () => {
|
||||
onChangeMode = (mode: string | number) => {
|
||||
if (mode == 'overview') {
|
||||
this.setState({ mode: mode }, () => {
|
||||
this.loadApplicationAppliedResources();
|
||||
});
|
||||
} else {
|
||||
this.setState({ mode: 'graph' }, () => {
|
||||
} else if (mode == 'resource-graph') {
|
||||
this.setState({ mode: mode }, () => {
|
||||
this.loadApplicationAppliedResources();
|
||||
});
|
||||
}
|
||||
|
@ -351,7 +363,6 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
resources,
|
||||
componentName,
|
||||
deployLoading,
|
||||
mode,
|
||||
} = this.state;
|
||||
const gatewayIPs: any = [];
|
||||
endpoints?.map((endpointObj) => {
|
||||
|
@ -381,6 +392,7 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
updateEnvs={() => {
|
||||
this.loadApplicationEnvbinding();
|
||||
this.loadApplicationWorkflows();
|
||||
this.loadApplicationPolicies();
|
||||
}}
|
||||
updateQuery={(params: { target?: string; component?: string }) => {
|
||||
this.updateQuery(params);
|
||||
|
@ -391,247 +403,282 @@ class ApplicationStatusPage extends React.Component<Props, State> {
|
|||
dispatch={this.props.dispatch}
|
||||
/>
|
||||
</Loading>
|
||||
<If condition={applicationStatus}>
|
||||
<Switch
|
||||
unCheckedChildren="Table"
|
||||
checkedChildren="Graph"
|
||||
defaultChecked={true}
|
||||
onChange={this.onChangeMode}
|
||||
/>
|
||||
</If>
|
||||
<Loading visible={loading && resourceLoading} style={{ width: '100%' }}>
|
||||
<If condition={applicationStatus && mode === 'graph'}>
|
||||
<ApplicationGraph application={applicationDetail} env={env} resources={resources} />
|
||||
</If>
|
||||
<If condition={applicationStatus && mode === 'table'}>
|
||||
<Card
|
||||
locale={locale().Card}
|
||||
contentHeight="200px"
|
||||
title={<Translation>Applied Resources</Translation>}
|
||||
>
|
||||
<Table locale={locale().Table} dataSource={resources}>
|
||||
<Table.Column
|
||||
dataIndex="cluster"
|
||||
title={<Translation>Cluster</Translation>}
|
||||
width="200px"
|
||||
cell={(v: string) => {
|
||||
let clusterName = v;
|
||||
if (!clusterName) {
|
||||
clusterName = 'Local';
|
||||
}
|
||||
if (
|
||||
checkPermission(
|
||||
{ resource: 'cluster:*', action: 'list' },
|
||||
applicationDetail?.project?.name,
|
||||
userInfo,
|
||||
)
|
||||
) {
|
||||
return <Link to="/clusters">{clusterName}</Link>;
|
||||
}
|
||||
return <span>{clusterName}</span>;
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="name"
|
||||
width="240px"
|
||||
title={<Translation>Namespace/Name</Translation>}
|
||||
cell={(v: string, i: number, row: AppliedResource) => {
|
||||
return `${row.namespace}/${row.name}`;
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
width="200px"
|
||||
dataIndex="kind"
|
||||
title={<Translation>Kind</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="apiVersion"
|
||||
title={<Translation>APIVersion</Translation>}
|
||||
/>
|
||||
<Table.Column dataIndex="component" title={<Translation>Component</Translation>} />
|
||||
<Table.Column
|
||||
dataIndex="deployVersion"
|
||||
title={<Translation>Revision</Translation>}
|
||||
cell={(v: string, i: number, row: AppliedResource) => {
|
||||
if (row.latest) {
|
||||
return (
|
||||
<span>
|
||||
<Icon
|
||||
style={{ color: 'green', marginRight: '8px' }}
|
||||
type="NEW"
|
||||
title="latest version resource"
|
||||
/>
|
||||
<Link to={`/applications/${applicationDetail?.name}/revisions`}>{v}</Link>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Link to={`/applications/${applicationDetail?.name}/revisions`}>{v}</Link>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Card>
|
||||
<If condition={componentStatus}>
|
||||
<Card
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px', marginBottom: '16px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Component Status</Translation>}
|
||||
>
|
||||
<Table locale={locale().Table} className="customTable" dataSource={componentStatus}>
|
||||
<Table.Column
|
||||
align="left"
|
||||
dataIndex="name"
|
||||
style={{ width: '17%' }}
|
||||
title={<Translation>Name</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
align="left"
|
||||
dataIndex="healthy"
|
||||
width="100px"
|
||||
cell={(v: boolean) => {
|
||||
if (v) {
|
||||
return (
|
||||
<div>
|
||||
<span className="circle circle-success" />
|
||||
<span>Healthy</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<span className="circle circle-warning" />
|
||||
<span>UnHealthy</span>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
title={<Translation>Healthy</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
align="left"
|
||||
dataIndex="trait"
|
||||
cell={(v: boolean, i: number, record: ComponentStatus) => {
|
||||
const { traits } = record;
|
||||
const Tags = (traits || []).map((item) => {
|
||||
if (item.healthy) {
|
||||
return (
|
||||
<Tag type="normal" size="small">
|
||||
<Tab onChange={this.onChangeMode} shape="capsule">
|
||||
<Tab.Item title={i18n.t('Overview')} key="overview">
|
||||
<Loading visible={loading && resourceLoading} style={{ width: '100%' }}>
|
||||
<If condition={applicationStatus}>
|
||||
<If condition={componentStatus}>
|
||||
<Card
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px', marginBottom: '16px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Component Status</Translation>}
|
||||
>
|
||||
<Table
|
||||
locale={locale().Table}
|
||||
className="customTable"
|
||||
dataSource={componentStatus}
|
||||
>
|
||||
<Table.Column
|
||||
align="left"
|
||||
dataIndex="name"
|
||||
style={{ width: '17%' }}
|
||||
title={<Translation>Name</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="cluster"
|
||||
title={<Translation>Cluster</Translation>}
|
||||
width="200px"
|
||||
cell={(v: string) => {
|
||||
let clusterName = v;
|
||||
if (!clusterName) {
|
||||
clusterName = 'Local';
|
||||
}
|
||||
if (
|
||||
checkPermission(
|
||||
{ resource: 'cluster:*', action: 'list' },
|
||||
applicationDetail?.project?.name,
|
||||
userInfo,
|
||||
)
|
||||
) {
|
||||
return <Link to="/clusters">{clusterName}</Link>;
|
||||
}
|
||||
return <span>{clusterName}</span>;
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
align="left"
|
||||
dataIndex="healthy"
|
||||
width="100px"
|
||||
cell={(v: boolean) => {
|
||||
if (v) {
|
||||
return (
|
||||
<div>
|
||||
<span className="circle circle-success" />
|
||||
<span>{item.type}</span>
|
||||
<span>Healthy</span>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Tag type="normal" size="small">
|
||||
<div>
|
||||
<span className="circle circle-failure" />
|
||||
<span>{item.type}</span>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
});
|
||||
return <TagGroup className="tags-content">{Tags}</TagGroup>;
|
||||
}}
|
||||
title={<Translation>Traits</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
align="center"
|
||||
dataIndex="message"
|
||||
style={{ width: '50%' }}
|
||||
title={<Translation>Message</Translation>}
|
||||
cell={(v: string, i: number, record: ComponentStatus) => {
|
||||
const { message = '', traits } = record;
|
||||
const TraitMessages = (traits || []).map((item) => {
|
||||
if (item.message) {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<span>{item.type}: </span>
|
||||
<span>{item.message}</span>
|
||||
<span className="circle circle-warning" />
|
||||
<span>UnHealthy</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<div>{message}</div>
|
||||
{TraitMessages}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Card>
|
||||
</If>
|
||||
<If condition={applicationStatus?.conditions}>
|
||||
<Card
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Conditions</Translation>}
|
||||
>
|
||||
<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>
|
||||
}}
|
||||
title={<Translation>Healthy</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
align="left"
|
||||
dataIndex="trait"
|
||||
cell={(v: boolean, i: number, record: ComponentStatus) => {
|
||||
const { traits } = record;
|
||||
const Tags = (traits || []).map((item) => {
|
||||
if (item.healthy) {
|
||||
return (
|
||||
<Tag type="normal" size="small">
|
||||
<div>
|
||||
<span className="circle circle-success" />
|
||||
<span>{item.type}</span>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Tag type="normal" size="small">
|
||||
<div>
|
||||
<span className="circle circle-failure" />
|
||||
<span>{item.type}</span>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
>
|
||||
{row.message}
|
||||
</Balloon>
|
||||
});
|
||||
return <TagGroup className="tags-content">{Tags}</TagGroup>;
|
||||
}}
|
||||
title={<Translation>Traits</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
align="center"
|
||||
dataIndex="message"
|
||||
title={<Translation>Message</Translation>}
|
||||
cell={(v: string, i: number, record: ComponentStatus) => {
|
||||
const { message = '', traits } = record;
|
||||
const TraitMessages = (traits || []).map((item) => {
|
||||
if (item.message) {
|
||||
return (
|
||||
<div>
|
||||
<span>{item.type}: </span>
|
||||
<span>{item.message}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<div>{message}</div>
|
||||
{TraitMessages}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Card>
|
||||
</If>
|
||||
<Card
|
||||
locale={locale().Card}
|
||||
contentHeight="200px"
|
||||
title={<Translation>Applied Resources</Translation>}
|
||||
>
|
||||
<Table locale={locale().Table} dataSource={resources}>
|
||||
<Table.Column
|
||||
dataIndex="name"
|
||||
width="240px"
|
||||
title={<Translation>Namespace/Name</Translation>}
|
||||
cell={(v: string, i: number, row: AppliedResource) => {
|
||||
return `${row.namespace}/${row.name}`;
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="cluster"
|
||||
title={<Translation>Cluster</Translation>}
|
||||
width="200px"
|
||||
cell={(v: string) => {
|
||||
let clusterName = v;
|
||||
if (!clusterName) {
|
||||
clusterName = 'Local';
|
||||
}
|
||||
if (
|
||||
checkPermission(
|
||||
{ resource: 'cluster:*', action: 'list' },
|
||||
applicationDetail?.project?.name,
|
||||
userInfo,
|
||||
)
|
||||
) {
|
||||
return <Link to="/clusters">{clusterName}</Link>;
|
||||
}
|
||||
return <span>{clusterName}</span>;
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
width="200px"
|
||||
dataIndex="kind"
|
||||
title={<Translation>Kind</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="apiVersion"
|
||||
title={<Translation>APIVersion</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="component"
|
||||
title={<Translation>Component</Translation>}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex="deployVersion"
|
||||
title={<Translation>Revision</Translation>}
|
||||
cell={(v: string, i: number, row: AppliedResource) => {
|
||||
if (row.latest) {
|
||||
return (
|
||||
<span>
|
||||
<Icon
|
||||
style={{ color: 'green', marginRight: '8px' }}
|
||||
type="NEW"
|
||||
title="latest version resource"
|
||||
/>
|
||||
<Link to={`/applications/${applicationDetail?.name}/revisions`}>
|
||||
{v}
|
||||
</Link>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Link to={`/applications/${applicationDetail?.name}/revisions`}>{v}</Link>
|
||||
);
|
||||
}
|
||||
return <span>{v}</span>;
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Card>
|
||||
</If>
|
||||
</If>
|
||||
<If condition={!applicationStatus}>
|
||||
<div className="deployNotice">
|
||||
<div className="noticeBox">
|
||||
<h2>
|
||||
<Translation>Not Deploy</Translation>
|
||||
</h2>
|
||||
<div className="desc">
|
||||
<Translation>The current environment has not been deployed.</Translation>
|
||||
</div>
|
||||
<div className="noticeAction">
|
||||
<Button
|
||||
loading={deployLoading}
|
||||
disabled={applicationDetail?.readOnly}
|
||||
onClick={() => this.onDeploy()}
|
||||
type="primary"
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
</Card>
|
||||
|
||||
<If condition={applicationStatus?.conditions}>
|
||||
<Card
|
||||
locale={locale().Card}
|
||||
style={{ marginTop: '8px' }}
|
||||
contentHeight="auto"
|
||||
title={<Translation>Conditions</Translation>}
|
||||
>
|
||||
<Translation>Deploy</Translation>
|
||||
</Button>
|
||||
<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>
|
||||
<If condition={!applicationStatus}>
|
||||
<div className="deployNotice">
|
||||
<div className="noticeBox">
|
||||
<h2>
|
||||
<Translation>Not Deploy</Translation>
|
||||
</h2>
|
||||
<div className="desc">
|
||||
<Translation>The current environment has not been deployed.</Translation>
|
||||
</div>
|
||||
<div className="noticeAction">
|
||||
<Button
|
||||
loading={deployLoading}
|
||||
disabled={applicationDetail?.readOnly}
|
||||
onClick={() => this.onDeploy()}
|
||||
type="primary"
|
||||
>
|
||||
<Translation>Deploy</Translation>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</If>
|
||||
</Loading>
|
||||
</If>
|
||||
</Loading>
|
||||
</Tab.Item>
|
||||
<Tab.Item title={i18n.t('Resource Graph')} key="resource-graph">
|
||||
<Loading visible={loading && resourceLoading} style={{ width: '100%' }}>
|
||||
<If condition={applicationStatus}>
|
||||
<ApplicationGraph
|
||||
applicationStatus={applicationStatus}
|
||||
application={applicationDetail}
|
||||
env={env}
|
||||
resources={resources}
|
||||
/>
|
||||
</If>
|
||||
</Loading>
|
||||
</Tab.Item>
|
||||
</Tab>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash';
|
|||
import type { DiagramMakerNode } from 'diagram-maker';
|
||||
import type { WorkFlowNodeType } from '../entity';
|
||||
|
||||
import { Grid, Field, Form, Select, Input, Message, Button } from '@b-design/ui';
|
||||
import { Grid, Field, Form, Select, Input, Message, Button, Icon } from '@b-design/ui';
|
||||
import type { Rule } from '@alifd/field';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import Group from '../../../extends/Group';
|
||||
|
@ -35,6 +35,7 @@ type Props = {
|
|||
type State = {
|
||||
definitionDetail?: DefinitionDetail;
|
||||
definitionLoading: boolean;
|
||||
propertiesMode: 'native' | 'code';
|
||||
};
|
||||
|
||||
@connect()
|
||||
|
@ -45,6 +46,7 @@ class WorkflowForm extends Component<Props, State> {
|
|||
super(props);
|
||||
this.state = {
|
||||
definitionLoading: false,
|
||||
propertiesMode: 'native',
|
||||
};
|
||||
this.field = new Field(this, {
|
||||
onChange: (name: string, value: string) => {
|
||||
|
@ -129,7 +131,7 @@ class WorkflowForm extends Component<Props, State> {
|
|||
const { Row, Col } = Grid;
|
||||
const FormItem = Form.Item;
|
||||
const { t, closeDrawer, data, checkStepName } = this.props;
|
||||
const { definitionDetail } = this.state;
|
||||
const { definitionDetail, propertiesMode } = this.state;
|
||||
const validator = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||
this.uiSchemaRef.current?.validate(callback);
|
||||
};
|
||||
|
@ -260,8 +262,37 @@ class WorkflowForm extends Component<Props, State> {
|
|||
required={true}
|
||||
hasToggleIcon={true}
|
||||
>
|
||||
<If condition={definitionDetail && definitionDetail.uiSchema}>
|
||||
<If condition={definitionDetail}>
|
||||
<FormItem required={true}>
|
||||
<If condition={definitionDetail && definitionDetail.uiSchema}>
|
||||
<div className="flexright">
|
||||
<Button
|
||||
style={{ marginTop: '-12px' }}
|
||||
onClick={() => {
|
||||
if (propertiesMode === 'native') {
|
||||
this.setState({ propertiesMode: 'code' });
|
||||
} else {
|
||||
this.setState({ propertiesMode: 'native' });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<If condition={propertiesMode === 'native'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'display-code'}
|
||||
title={'Switch to code mode'}
|
||||
/>
|
||||
</If>
|
||||
<If condition={propertiesMode === 'code'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'laptop'}
|
||||
title={'Switch to native mode'}
|
||||
/>
|
||||
</If>
|
||||
</Button>
|
||||
</div>
|
||||
</If>
|
||||
<UISchema
|
||||
{...init(`properties`, {
|
||||
rules: [
|
||||
|
@ -271,7 +302,13 @@ class WorkflowForm extends Component<Props, State> {
|
|||
},
|
||||
],
|
||||
})}
|
||||
enableCodeEdit={propertiesMode === 'code'}
|
||||
uiSchema={definitionDetail && definitionDetail.uiSchema}
|
||||
definition={{
|
||||
type: 'workflowstep',
|
||||
name: definitionDetail?.name || '',
|
||||
description: definitionDetail?.description || '',
|
||||
}}
|
||||
ref={this.uiSchemaRef}
|
||||
mode={this.props.data ? 'edit' : 'new'}
|
||||
/>
|
||||
|
|
|
@ -12,6 +12,8 @@ import SelectSearch from './components/SelectSearch';
|
|||
import locale from '../../utils/locale';
|
||||
import { getMatchParamObj } from '../../utils/utils';
|
||||
import './index.less';
|
||||
import { checkPermission } from '../../utils/permission';
|
||||
import type { LoginUserInfo } from '../../interface/user';
|
||||
|
||||
type Props = {
|
||||
match: {
|
||||
|
@ -19,6 +21,7 @@ type Props = {
|
|||
definitionType: string;
|
||||
};
|
||||
};
|
||||
userInfo?: LoginUserInfo;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -30,7 +33,7 @@ type State = {
|
|||
};
|
||||
|
||||
@connect((store: any) => {
|
||||
return { ...store.definitions };
|
||||
return { ...store.definitions, ...store.user };
|
||||
})
|
||||
class Definitions extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
|
@ -62,10 +65,14 @@ class Definitions extends Component<Props, State> {
|
|||
}
|
||||
|
||||
lisDefinitions() {
|
||||
const { userInfo } = this.props;
|
||||
const { definitionType } = this.state;
|
||||
if (!definitionType) {
|
||||
return;
|
||||
}
|
||||
if (!checkPermission({ resource: 'definition:*', action: 'list' }, '', userInfo)) {
|
||||
return;
|
||||
}
|
||||
const params = {
|
||||
definitionType,
|
||||
queryAll: true,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.table-delivery-list {
|
||||
.table-env-list {
|
||||
overflow: auto;
|
||||
.next-table-cell.last .next-table-cell-wrapper {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
@ -80,14 +80,14 @@ class TableList extends Component<Props> {
|
|||
return <span>{v}</span>;
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'project',
|
||||
// title: <Translation>Project</Translation>,
|
||||
// dataIndex: 'project',
|
||||
// cell: (v: NameAlias) => {
|
||||
// return <span>{v.alias || v.name}</span>;
|
||||
// },
|
||||
// },
|
||||
{
|
||||
key: 'project',
|
||||
title: <Translation>Project</Translation>,
|
||||
dataIndex: 'project',
|
||||
cell: (v: NameAlias) => {
|
||||
return <span>{v.alias || v.name}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
title: <Translation>Description</Translation>,
|
||||
|
@ -118,6 +118,7 @@ class TableList extends Component<Props> {
|
|||
key: 'operation',
|
||||
title: <Translation>Actions</Translation>,
|
||||
dataIndex: 'operation',
|
||||
width: '120px',
|
||||
cell: (v: string, i: number, record: Env) => {
|
||||
return (
|
||||
<div>
|
||||
|
@ -171,11 +172,12 @@ class TableList extends Component<Props> {
|
|||
const columns = this.getColumns();
|
||||
const { list } = this.props;
|
||||
return (
|
||||
<div className="table-delivery-list margin-top-20">
|
||||
<div className="table-env-list margin-top-20">
|
||||
<Table
|
||||
locale={locale().Table}
|
||||
className="customTable"
|
||||
size="medium"
|
||||
style={{ minWidth: '1200px' }}
|
||||
dataSource={list}
|
||||
hasBorder={false}
|
||||
loading={false}
|
||||
|
|
|
@ -103,7 +103,7 @@ class CreateIntegration extends React.Component<Props, State> {
|
|||
createConfigType(queryData, params)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
Message.success(<Translation>Create config success</Translation>);
|
||||
Message.success(<Translation>Integration config created successfully</Translation>);
|
||||
this.props.onCreate();
|
||||
}
|
||||
})
|
||||
|
|
|
@ -102,8 +102,8 @@ class Integrations extends Component<Props, State> {
|
|||
if (params) {
|
||||
deleteConfig(params).then((res) => {
|
||||
if (res) {
|
||||
Message.success(<Translation>Delete config success</Translation>);
|
||||
this.listIntegrations();
|
||||
Message.success(<Translation>Integration config deleted successfully</Translation>);
|
||||
setTimeout(() => this.listIntegrations(), 2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ class Integrations extends Component<Props, State> {
|
|||
|
||||
onCreate = () => {
|
||||
this.setState({ visible: false });
|
||||
this.listIntegrations();
|
||||
setTimeout(() => this.listIntegrations(), 2000);
|
||||
};
|
||||
|
||||
onCloseCreate = () => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.table-delivery-list {
|
||||
overflow: auto;
|
||||
.next-table-cell.last .next-table-cell-wrapper {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
|
@ -140,6 +140,7 @@ class TableList extends Component<Props> {
|
|||
locale={locale().Table}
|
||||
className="customTable"
|
||||
size="medium"
|
||||
style={{ minWidth: '1200px' }}
|
||||
dataSource={list}
|
||||
hasBorder={false}
|
||||
loading={false}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'mocha';
|
||||
import { assert } from 'chai';
|
||||
import { resourceMatch, ResourceName } from '../permission';
|
||||
import { equalArray } from '../common';
|
||||
|
||||
describe('test permission', () => {
|
||||
it('test resourceMatch', () => {
|
||||
|
@ -14,3 +15,11 @@ describe('test permission', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test util', () => {
|
||||
it('test equal the array', () => {
|
||||
assert.equal(equalArray(['a', 'b'], ['b', 'a']), true);
|
||||
assert.equal(equalArray(['b'], ['b', 'a']), false);
|
||||
assert.equal(equalArray(undefined, ['b', 'a']), false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -89,6 +89,43 @@ export function momentShortDate(time: undefined | string): string {
|
|||
return moment(time).format('YYYY/MM/DD');
|
||||
}
|
||||
|
||||
export function beautifyTime(time?: string) {
|
||||
if (!time) {
|
||||
return '';
|
||||
}
|
||||
const timestamp = moment(time).unix();
|
||||
const now = new Date();
|
||||
let mistiming = Math.round(now.getTime() / 1000) - timestamp;
|
||||
const postfix = mistiming > 0 ? 'ago' : 'later';
|
||||
mistiming = Math.abs(mistiming);
|
||||
const arrr = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'];
|
||||
const arrn = [31536000, 2592000, 604800, 86400, 3600, 60, 1];
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const inm = Math.floor(mistiming / arrn[i]);
|
||||
if (inm != 0) {
|
||||
return inm + ' ' + arrr[i] + ' ' + postfix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function beautifyBinarySize(value?: number) {
|
||||
if (null == value || value == 0) {
|
||||
return '0 Bytes';
|
||||
}
|
||||
const unitArr = new Array('Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
|
||||
let index = 0;
|
||||
index = Math.floor(Math.log(value) / Math.log(1024));
|
||||
const size = value / Math.pow(1024, index);
|
||||
let sizeStr = '';
|
||||
if (size % 1 === 0) {
|
||||
sizeStr = size.toFixed(0);
|
||||
} else {
|
||||
sizeStr = size.toFixed(2);
|
||||
}
|
||||
return sizeStr + unitArr[index];
|
||||
}
|
||||
|
||||
export const checkName = /^[a-z]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/;
|
||||
export const urlRegular =
|
||||
/(https|http|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g;
|
||||
|
@ -171,3 +208,24 @@ export function isMatchBusinessCode(businessCode: number) {
|
|||
const tokenExpiredList = [12002, 12010];
|
||||
return tokenExpiredList.includes(businessCode);
|
||||
}
|
||||
|
||||
export function equalArray(a?: string[], b?: string[]) {
|
||||
if (a === undefined && b === undefined) {
|
||||
return true;
|
||||
}
|
||||
if (b === undefined || a === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
} else {
|
||||
const sa = a.sort();
|
||||
const sb = b.sort();
|
||||
for (let i = 0; i < sa.length; i++) {
|
||||
if (sa[i] !== sb[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
74
yarn.lock
74
yarn.lock
|
@ -88,6 +88,29 @@
|
|||
resolved "https://registry.nlark.com/@alifd/validate/download/@alifd/validate-1.2.3.tgz?cache=0&sync_timestamp=1626662152988&other_urls=https%3A%2F%2Fregistry.nlark.com%2F%40alifd%2Fvalidate%2Fdownload%2F%40alifd%2Fvalidate-1.2.3.tgz"
|
||||
integrity sha1-O3+IChqVVHCzA/3c5a+kKp8tn/I=
|
||||
|
||||
"@ant-design/colors@^3.1.0":
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-3.2.2.tgz#5ad43d619e911f3488ebac303d606e66a8423903"
|
||||
integrity sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==
|
||||
dependencies:
|
||||
tinycolor2 "^1.4.1"
|
||||
|
||||
"@ant-design/icons-svg@^4.0.0":
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz#8630da8eb4471a4aabdaed7d1ff6a97dcb2cf05a"
|
||||
integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==
|
||||
|
||||
"@ant-design/icons@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.0.0.tgz#cdb870777f185422cfc0e235392592d3e4795ef6"
|
||||
integrity sha512-oJTNTyo/6DjVmK/DSa58A+7gZRGgzBCAEJ9Pfa6U+BAZO28tvOE3YzlQd9gq9Qu6d47JL1ixyID3qsmRFqitlQ==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^3.1.0"
|
||||
"@ant-design/icons-svg" "^4.0.0"
|
||||
classnames "^2.2.6"
|
||||
insert-css "^2.0.0"
|
||||
rc-util "^4.9.0"
|
||||
|
||||
"@b-design/ui@^1.0.63":
|
||||
version "1.0.63"
|
||||
resolved "https://registry.npmmirror.com/@b-design/ui/download/@b-design/ui-1.0.63.tgz"
|
||||
|
@ -2139,6 +2162,13 @@ acorn@^7.1.0, acorn@^7.4.0:
|
|||
resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
|
||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||
|
||||
add-dom-event-listener@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310"
|
||||
integrity sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==
|
||||
dependencies:
|
||||
object-assign "4.x"
|
||||
|
||||
address@1.1.2, address@^1.0.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/address/-/address-1.1.2.tgz"
|
||||
|
@ -5289,6 +5319,11 @@ getpass@^0.1.1:
|
|||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
github-markdown-css@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.npmmirror.com/github-markdown-css/-/github-markdown-css-5.1.0.tgz#a96281cd90a8969e3c13b9d3ca6a733a523a00a6"
|
||||
integrity sha512-QLtORwHHtUHhPMHu7i4GKfP6Vx5CWZn+NKQXe+cBhslY1HEt0CTEkP4d/vSROKV0iIJSpl4UtlQ16AD8C6lMug==
|
||||
|
||||
glob-parent@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmmirror.com/glob-parent/download/glob-parent-3.1.0.tgz?cache=0&sync_timestamp=1632953810778&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fglob-parent%2Fdownload%2Fglob-parent-3.1.0.tgz"
|
||||
|
@ -5920,6 +5955,11 @@ inline-style-parser@0.1.1:
|
|||
resolved "https://registry.npm.taobao.org/inline-style-parser/download/inline-style-parser-0.1.1.tgz"
|
||||
integrity sha1-7Io7QpJ06cCh8cT/qUU6f+9yzqE=
|
||||
|
||||
insert-css@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/insert-css/-/insert-css-2.0.0.tgz#eb5d1097b7542f4c79ea3060d3aee07d053880f4"
|
||||
integrity sha512-xGq5ISgcUP5cvGkS2MMFLtPDBtrtQPSFfC6gA6U8wHKqfjTIMZLZNxOItQnoSjdOzlXOLU/yD32RKC4SvjNbtA==
|
||||
|
||||
internal-ip@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmmirror.com/internal-ip/download/internal-ip-4.3.0.tgz"
|
||||
|
@ -7904,7 +7944,7 @@ oauth-sign@~0.9.0:
|
|||
resolved "https://registry.nlark.com/oauth-sign/download/oauth-sign-0.9.0.tgz"
|
||||
integrity sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=
|
||||
|
||||
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||
object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
@ -9229,6 +9269,15 @@ prop-types@^15.0.0, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1,
|
|||
object-assign "^4.1.1"
|
||||
react-is "^16.8.1"
|
||||
|
||||
prop-types@^15.5.10:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
property-information@^6.0.0:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.nlark.com/property-information/download/property-information-6.0.1.tgz"
|
||||
|
@ -9421,6 +9470,17 @@ rax@^1.1.0:
|
|||
rax-create-factory "^1.0.0"
|
||||
rax-is-valid-element "^1.0.0"
|
||||
|
||||
rc-util@^4.9.0:
|
||||
version "4.21.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.21.1.tgz#88602d0c3185020aa1053d9a1e70eac161becb05"
|
||||
integrity sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==
|
||||
dependencies:
|
||||
add-dom-event-listener "^1.1.0"
|
||||
prop-types "^15.5.10"
|
||||
react-is "^16.12.0"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
shallowequal "^1.1.0"
|
||||
|
||||
react-cookies@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.npm.taobao.org/react-cookies/download/react-cookies-0.1.1.tgz"
|
||||
|
@ -9508,7 +9568,12 @@ react-i18next@11.13.0:
|
|||
"@babel/runtime" "^7.14.5"
|
||||
html-parse-stringify "^3.0.1"
|
||||
|
||||
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||
react-icons@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703"
|
||||
integrity sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==
|
||||
|
||||
react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
@ -11135,6 +11200,11 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3:
|
|||
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
tinycolor2@^1.4.1:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
|
||||
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
|
||||
|
||||
to-arraybuffer@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz"
|
||||
|
|
Loading…
Reference in New Issue