Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cosnole): when create workload add register dialog - for master #2143

Merged
merged 2 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { EditResourceContainerAdvancedPanel } from './EditResourceContainerAdvan
import { EditResourceContainerEnvItem } from './EditResourceContainerEnvItem';
import { EditResourceContainerLimitItem } from './EditResourceContainerLimitItem';
import { EditResourceContainerMountItem } from './EditResourceContainerMountItem';
import { RegistrySelectDialog, RegistryTagSelectDialog } from './registrySelect';

interface ContainerItemProps extends RootProps {
/** 容器的id */
Expand All @@ -43,6 +44,7 @@ const mapDispatchToProps = dispatch =>

@connect(state => state, mapDispatchToProps)
export class EditResourceContainerItem extends React.Component<ContainerItemProps, {}> {
state: Readonly<{ tags: any[] }> = { tags: null };
render() {
let { actions, subRoot, cKey, clusterVersion, cluster } = this.props,
{ workloadEdit, addons } = subRoot,
Expand Down Expand Up @@ -132,22 +134,39 @@ export class EditResourceContainerItem extends React.Component<ContainerItemProp
value={container.registry}
onChange={e => {
actions.editWorkload.updateContainer({ registry: e.target.value }, cKey);

this.setState({ tags: null });
}}
onBlur={e => actions.validate.workload.validateRegistrySelection(e.target.value, cKey)}
/>
</Bubble>

<RegistrySelectDialog
onConfirm={({ registry, tags }) => {
actions.editWorkload.updateContainer({ registry }, cKey);

this.setState({ tags });
}}
/>
</div>
</FormItem>
<FormItem label={t('镜像版本(Tag)')} className="tag-mod">
<div className="tc-15-autocomplete xl">
<input
type="text"
className="tc-15-input-text m"
className="tc-15-input-text m mr10"
value={container.tag}
onChange={e => {
actions.editWorkload.updateContainer({ tag: e.target.value }, cKey);
}}
/>

{this.state.tags && (
<RegistryTagSelectDialog
tags={this.state.tags}
onConfirm={tag => actions.editWorkload.updateContainer({ tag }, cKey)}
/>
)}
</div>
</FormItem>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { RegistrySelectDialog } from './registrySelectDialog';

export { RegistryTagSelectDialog } from './registryTagSelectDialog';
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import React, { useMemo, useState } from 'react';
import { Button, Modal, Table, SearchBox, TableColumn, Text } from 'tea-component';
import { fetchRepositoryList } from '@src/webApi/registry';
import { useRequest } from 'ahooks';
import { fetchDockerRegUrl } from '@src/modules/registry/WebAPI';

const { filterable, scrollable, radioable } = Table.addons;

const ALL_VALUE = '__ALL__';

export const RegistrySelectDialog = ({ onConfirm }) => {
const [visible, setVisible] = useState(false);

const [selectedRepo, setSelectedRepo] = useState(null);

const [searchValue, setSearchValue] = useState('');

const [visibilityType, setVisibilityType] = useState(ALL_VALUE);

const [selectedNamespaceList, setSelectedNamespaceList] = useState([]);

const { data: repoBaseUrl } = useRequest(async () => {
const res = await fetchDockerRegUrl();

console.log('repoBaseUrl---->', res);

return res;
});

const { data: _repositoryList } = useRequest(async () => {
const res = await fetchRepositoryList();

return res?.items ?? [];
});

const repositoryList = useMemo(() => {
return (
_repositoryList
?.filter(repo => repo?.spec?.name?.includes(searchValue))
?.filter(repo => visibilityType === ALL_VALUE || repo?.spec?.visibility === visibilityType)
?.filter(
repo => selectedNamespaceList?.length === 0 || selectedNamespaceList?.includes(repo?.spec?.namespaceName)
) ?? []
);
}, [_repositoryList, searchValue, visibilityType, selectedNamespaceList]);

const namespaceOptions = useMemo(() => {
return _repositoryList?.map(repo => ({ value: repo?.spec?.namespaceName })) ?? [];
}, [_repositoryList]);

const columns: TableColumn[] = [
{
key: 'spec.name',
header: '名称'
},

{
key: 'spec.visibility',
header: '类型'
},

{
key: 'spec.namespaceName',
header: '命名空间'
},

{
key: 'spec.resourceVersion',
header: '仓库地址',
render(repo) {
return <Text overflow copyable>{`${repoBaseUrl}/${repo?.spec?.namespaceName}/${repo?.spec?.name}`}</Text>;
}
}
];

return (
<>
<Button type="link" onClick={() => setVisible(true)}>
选择镜像
</Button>

<Modal caption="选择镜像" visible={visible} onClose={() => setVisible(false)}>
<Modal.Body>
<Table.ActionPanel>
<SearchBox value={searchValue} onChange={value => setSearchValue(value)} />
</Table.ActionPanel>

<Table
columns={columns}
records={repositoryList}
recordKey="metadata.name"
addons={[
filterable({
type: 'single',
column: 'spec.visibility',
value: visibilityType,
onChange: value => setVisibilityType(value),
all: {
value: ALL_VALUE,
text: '全部'
},
// 选项列表
options: [
{ value: 'Public', text: '公有' },
{ value: 'Private', text: '私有' }
]
}),

filterable({
type: 'multiple',
column: 'spec.namespaceName',
value: selectedNamespaceList,
onChange: value => {
setSelectedNamespaceList(value);
},
all: {
value: ALL_VALUE,
text: '全部'
},
options: namespaceOptions
}),

scrollable({
maxHeight: 500
}),

radioable({
value: selectedRepo,
onChange(key) {
setSelectedRepo(key);
},
rowSelect: true
})
]}
/>
</Modal.Body>

<Modal.Footer>
<Button
disabled={!selectedRepo}
type="primary"
onClick={() => {
const repo = _repositoryList?.find(item => item?.metadata?.name === selectedRepo);

const registry = `${repoBaseUrl}/${repo?.spec?.namespaceName}/${repo?.spec?.name}`;
onConfirm({ registry, tags: repo?.status?.tags ?? null });
setVisible(false);
}}
>
确认
</Button>

<Button onClick={() => setVisible(false)}>取消</Button>
</Modal.Footer>
</Modal>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useState } from 'react';
import { Button, Modal, Table, Checkbox, TableColumn, Text } from 'tea-component';

const { scrollable, radioable } = Table.addons;

export const RegistryTagSelectDialog = ({ tags, onConfirm }) => {
const [visible, setVisible] = useState(false);

const [selectedTag, setSelectedTag] = useState(null);

const columns: TableColumn[] = [
{
key: 'name',
header: '镜像版本'
},

{
key: 'digest',
header: '摘要(SHA256)',
render({ digest }) {
return <Text copyable>{digest}</Text>;
}
}
];

return (
<>
<Button type="link" onClick={() => setVisible(true)}>
选择镜像版本
</Button>

<Modal caption="选择镜像版本" visible={visible} onClose={() => setVisible(false)}>
<Modal.Body>
<Table
columns={columns}
records={tags}
recordKey="name"
addons={[
scrollable({
maxHeight: 500
}),

radioable({
value: selectedTag,
onChange(key) {
setSelectedTag(key);
},
rowSelect: true
})
]}
/>
</Modal.Body>

<Modal.Footer>
<Button
disabled={!selectedTag}
onClick={() => {
onConfirm(selectedTag);
setVisible(false);
}}
type="primary"
>
确认
</Button>

<Button onClick={() => setVisible(false)}>取消</Button>
</Modal.Footer>
</Modal>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { useState } from 'react';
import { Button, Modal, Form, TextArea } from 'tea-component';
import { registryApi } from '@src/webApi';

interface INamespaceDisplayNameEditorProps {
value: string;
name: string;
onSuccess: () => void;
}

export const NamespaceDisplayNameEditor = ({ value, name, onSuccess }: INamespaceDisplayNameEditorProps) => {
const [visible, _setVisible] = useState(false);

function setVisible(visible: boolean) {
_setVisible(visible);

setDisplayName(value);
}

const [displayName, setDisplayName] = useState(value);

async function handleSubmit() {
await registryApi.modifyNamespaceDisplayName({ name, displayName });

onSuccess();

setVisible(false);
}

return (
<>
<Button type="icon" icon="pencil" onClick={() => setVisible(true)} />

<Modal visible={visible} caption="编辑描述信息" onClose={() => setVisible(false)}>
<Modal.Body>
<Form>
<Form.Item label="描述" required>
<TextArea size="full" value={displayName} onChange={value => setDisplayName(value.trim())} />
</Form.Item>
</Form>
</Modal.Body>

<Modal.Footer>
<Button type="primary" disabled={!displayName} onClick={handleSubmit}>
确定
</Button>

<Button onClick={() => setVisible(false)}>取消</Button>
</Modal.Footer>
</Modal>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DialogBodyLayout } from '../../../common/layouts';
import { Repo } from '../../models';
import { router } from '../../router';
import { RootProps } from '../RegistryApp';
import { NamespaceDisplayNameEditor } from './NamespaceDisplayNameEditor';

export class RepoTablePanel extends React.Component<RootProps, any> {
componentDidMount() {
Expand Down Expand Up @@ -96,6 +97,11 @@ export class RepoTablePanel extends React.Component<RootProps, any> {
render: (x: Repo) => (
<Text parent="div" overflow>
<span className="text">{x.spec.displayName}</span>
<NamespaceDisplayNameEditor
value={x?.spec?.displayName ?? ''}
name={x?.metadata?.name}
onSuccess={() => this.props.actions.repo.fetch()}
/>
</Text>
)
},
Expand Down
2 changes: 2 additions & 0 deletions web/console/src/webApi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export * as virtualMachineAPI from './virtual-machine';
export * as storageClassAPI from './storage-class';

export * as PVCAPI from './pvc';

export * as registryApi from './registry';
Loading