feat: 添加删除全部modal的方法

This commit is contained in:
cp3hnu 2024-05-24 09:02:09 +08:00
parent b138b3f241
commit e2cfc94482
20 changed files with 104 additions and 92 deletions

View File

@ -17,6 +17,7 @@ export enum ModelDeploymentStatus {
Running = 'Running', // 运行中 Running = 'Running', // 运行中
Stopped = 'Stopped', // 已停止 Stopped = 'Stopped', // 已停止
Failed = 'Failed', // 失败 Failed = 'Failed', // 失败
Pending = 'Pending', // 挂起中
} }
export const modelDeploymentStatusOptions = [ export const modelDeploymentStatusOptions = [
@ -25,4 +26,5 @@ export const modelDeploymentStatusOptions = [
{ label: '运行中', value: ModelDeploymentStatus.Running }, { label: '运行中', value: ModelDeploymentStatus.Running },
{ label: '已停止', value: ModelDeploymentStatus.Stopped }, { label: '已停止', value: ModelDeploymentStatus.Stopped },
{ label: '失败', value: ModelDeploymentStatus.Failed }, { label: '失败', value: ModelDeploymentStatus.Failed },
{ label: '挂起中', value: ModelDeploymentStatus.Pending },
]; ];

View File

@ -126,13 +126,3 @@ export const useResetFormOnCloseModal = (form: FormInstance, open: boolean) => {
} }
}, [form, prevOpen, open]); }, [form, prevOpen, open]);
}; };
export const useInputModel = <T>(initialValue: T) => {
const [value, setValue] = useState<T>(initialValue);
const updateValue = useCallback((e: any) => {
setValue(e.target?.value);
}, []);
return [value, updateValue];
};

View File

@ -4,6 +4,7 @@ import { to } from '@/utils/promise';
import { type SelectProps } from 'antd'; import { type SelectProps } from 'antd';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
// 获取资源规格
export function useComputingResource() { export function useComputingResource() {
const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]); const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]);

View File

@ -1,6 +1,7 @@
import { getSessionStorageItem, removeSessionStorageItem } from '@/utils/sessionStorage'; import { getSessionStorageItem, removeSessionStorageItem } from '@/utils/sessionStorage';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
// 获取缓存数据
export function useSessionStorage<T>(key: string, isObject: boolean, initialValue: T) { export function useSessionStorage<T>(key: string, isObject: boolean, initialValue: T) {
const [storage, setStorage] = useState<T>(initialValue); const [storage, setStorage] = useState<T>(initialValue);

View File

@ -97,6 +97,11 @@ function AddExperimentModal({
wrapperCol: { span: 20 }, wrapperCol: { span: 20 },
}; };
const paramLayout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
// 除了流水线选择发生变化 // 除了流水线选择发生变化
const handleWorkflowChange = (id: string | number) => { const handleWorkflowChange = (id: string | number) => {
const pipeline: Workflow | undefined = workflowList.find((v) => v.id === id); const pipeline: Workflow | undefined = workflowList.find((v) => v.id === id);
@ -187,7 +192,7 @@ function AddExperimentModal({
fields.map(({ key, name, ...restField }) => ( fields.map(({ key, name, ...restField }) => (
<Form.Item <Form.Item
{...restField} {...restField}
{...layout} {...paramLayout}
key={key} key={key}
label={getParamType(globalParam[name])} label={getParamType(globalParam[name])}
name={[name, 'param_value']} name={[name, 'param_value']}

View File

@ -1,9 +1,7 @@
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { getComputingResourceReq } from '@/services/pipeline'; import { useComputingResource } from '@/hooks/resource';
import { PipelineNodeModelSerialize } from '@/types'; import { PipelineNodeModelSerialize } from '@/types';
import { to } from '@/utils/promise';
import { Form, Input, Select, type FormProps } from 'antd'; import { Form, Input, Select, type FormProps } from 'antd';
import { useEffect, useState } from 'react';
import styles from './index.less'; import styles from './index.less';
const { TextArea } = Input; const { TextArea } = Input;
@ -13,24 +11,7 @@ type ExperimentParameterProps = {
}; };
function ExperimentParameter({ form, nodeData }: ExperimentParameterProps) { function ExperimentParameter({ form, nodeData }: ExperimentParameterProps) {
const [resourceStandardList, setResourceStandardList] = useState([]); // 资源规模列表 const [resourceStandardList] = useComputingResource(); // 资源规模
useEffect(() => {
getComputingResource();
}, []);
// 获取资源规格列表数据
const getComputingResource = async () => {
const params = {
page: 0,
size: 1000,
resource_type: '',
};
const [res] = await to(getComputingResourceReq(params));
if (res && res.data && res.data.content) {
setResourceStandardList(res.data.content);
}
};
// 控制策略 // 控制策略
const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}).map( const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}).map(

View File

@ -18,7 +18,7 @@ export enum ExperimentStatus {
export const experimentStatusInfo: Record<ExperimentStatus, StatusInfo | undefined> = { export const experimentStatusInfo: Record<ExperimentStatus, StatusInfo | undefined> = {
Running: { Running: {
label: '运行中', label: '运行中',
color: '#165bff', color: '#1664ff',
icon: '/assets/images/running-icon.png', icon: '/assets/images/running-icon.png',
}, },
Succeeded: { Succeeded: {
@ -53,7 +53,7 @@ export const experimentStatusInfo: Record<ExperimentStatus, StatusInfo | undefin
}, },
Omitted: { Omitted: {
label: '未执行', label: '未执行',
color: '#8a8a8ae', color: '#8a8a8a',
icon: '/assets/images/omitted-icon.png', icon: '/assets/images/omitted-icon.png',
}, },
}; };

View File

@ -174,13 +174,14 @@ function MirrorList() {
title: '版本数据', title: '版本数据',
dataIndex: 'version_count', dataIndex: 'version_count',
key: 'version_count', key: 'version_count',
width: 100, width: '15%',
render: CommonTableCell(), render: CommonTableCell(),
}, },
{ {
title: '镜像描述', title: '镜像描述',
dataIndex: 'description', dataIndex: 'description',
key: 'description', key: 'description',
width: '35%',
render: CommonTableCell(true), render: CommonTableCell(true),
ellipsis: { showTitle: false }, ellipsis: { showTitle: false },
}, },
@ -188,7 +189,7 @@ function MirrorList() {
title: '创建时间', title: '创建时间',
dataIndex: 'create_time', dataIndex: 'create_time',
key: 'create_time', key: 'create_time',
width: 200, width: '20%',
render: DateTableCell, render: DateTableCell,
}, },
{ {

View File

@ -1,7 +1,7 @@
/* /*
* @Author: * @Author:
* @Date: 2024-04-18 18:35:41 * @Date: 2024-04-18 18:35:41
* @Description: * @Description:
*/ */
import { MirrorVersionStatus } from '@/enums'; import { MirrorVersionStatus } from '@/enums';
import styles from './index.less'; import styles from './index.less';
@ -26,7 +26,7 @@ const statusInfo: Record<MirrorVersionStatus, MirrorVersionStatusInfo> = {
}, },
}; };
function MirrorStatusCell(status: MirrorVersionStatus) { function MirrorStatusCell(status?: MirrorVersionStatus | null) {
if (status === null || status === undefined || !statusInfo[status]) { if (status === null || status === undefined || !statusInfo[status]) {
return <span>--</span>; return <span>--</span>;
} }

View File

@ -164,7 +164,7 @@ function ModelDeploymentInfo() {
</Col> </Col>
<Col span={10}> <Col span={10}>
<div className={styles['model-deployment-info__basic__item']}> <div className={styles['model-deployment-info__basic__item']}>
<div className={styles['label']}></div> <div className={styles['label']}></div>
<div className={styles['value']}> <div className={styles['value']}>
{modelDeployementInfo?.resource {modelDeployementInfo?.resource
? getResourceDescription(modelDeployementInfo.resource) ? getResourceDescription(modelDeployementInfo.resource)
@ -174,7 +174,7 @@ function ModelDeploymentInfo() {
</Col> </Col>
</Row> </Row>
<Row gutter={40}> <Row gutter={40}>
<Col span={24}> <Col span={18}>
<div className={styles['model-deployment-info__basic__item']}> <div className={styles['model-deployment-info__basic__item']}>
<div className={styles['label']}>  </div> <div className={styles['label']}>  </div>
<div className={styles['value']}>{modelDeployementInfo?.description ?? '--'}</div> <div className={styles['value']}>{modelDeployementInfo?.description ?? '--'}</div>

View File

@ -249,7 +249,8 @@ function ModelDeployment() {
</Button> </Button>
)} )}
{(record.status === ModelDeploymentStatus.Running || {(record.status === ModelDeploymentStatus.Running ||
record.status === ModelDeploymentStatus.Init) && ( record.status === ModelDeploymentStatus.Init ||
record.status === ModelDeploymentStatus.Pending) && (
<Button <Button
type="link" type="link"
size="small" size="small"

View File

@ -6,10 +6,14 @@
} }
&--stopped { &--stopped {
color: @warning-color; color: @abort-color;
} }
&--error { &--error {
color: @error-color; color: @error-color;
} }
&--pending {
color: @warning-color;
}
} }

View File

@ -28,9 +28,13 @@ export const statusInfo: Record<ModelDeploymentStatus, ModelDeploymentStatusInfo
classname: styles['model-deployment-status-cell--error'], classname: styles['model-deployment-status-cell--error'],
text: '失败', text: '失败',
}, },
[ModelDeploymentStatus.Pending]: {
classname: styles['model-deployment-status-cell--pending'],
text: '挂起中',
},
}; };
function ModelDeploymentStatusCell(status: ModelDeploymentStatus | undefined) { function ModelDeploymentStatusCell(status?: ModelDeploymentStatus | null) {
if (status === null || status === undefined || !statusInfo[status]) { if (status === null || status === undefined || !statusInfo[status]) {
return <span>--</span>; return <span>--</span>;
} }

View File

@ -26,13 +26,7 @@ export type ModelDeploymentData = {
// 操作类型 // 操作类型
export enum ModelDeploymentOperationType { export enum ModelDeploymentOperationType {
Create = 'create', Create = 'Create',
Update = 'update', Update = 'Update',
Restart = 'restart', Restart = 'Restart',
} }
// 状态
export type ModelDeploymentStatusInfo = {
text: string;
classname: string;
};

View File

@ -1,14 +1,17 @@
import KFIcon from '@/components/KFIcon'; import KFIcon from '@/components/KFIcon';
import ParameterInput from '@/components/ParameterInput'; import ParameterInput from '@/components/ParameterInput';
import SubAreaTitle from '@/components/SubAreaTitle'; import SubAreaTitle from '@/components/SubAreaTitle';
import { getComputingResourceReq } from '@/services/pipeline'; import { useComputingResource } from '@/hooks/resource';
import { openAntdModal } from '@/utils/modal'; import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise'; import { to } from '@/utils/promise';
import { Button, Drawer, Form, Input, Select } from 'antd'; import { Button, Drawer, Form, Input, Select } from 'antd';
import { pick } from 'lodash'; import { pick } from 'lodash';
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; import { forwardRef, useImperativeHandle, useState } from 'react';
import PropsLabel from '../components/PropsLabel'; import PropsLabel from '../components/PropsLabel';
import ResourceSelectorModal, { ResourceSelectorType } from '../components/ResourceSelectorModal'; import ResourceSelectorModal, {
ResourceSelectorType,
selectorTypeConfig,
} from '../components/ResourceSelectorModal';
import styles from './props.less'; import styles from './props.less';
import { canInput, createMenuItems } from './utils'; import { canInput, createMenuItems } from './utils';
const { TextArea } = Input; const { TextArea } = Input;
@ -19,26 +22,9 @@ const Props = forwardRef(({ onParentChange }, ref) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [selectedModel, setSelectedModel] = useState(undefined); // const [selectedModel, setSelectedModel] = useState(undefined); //
const [selectedDataset, setSelectedDataset] = useState(undefined); // const [selectedDataset, setSelectedDataset] = useState(undefined); //
const [resourceStandardList, setResourceStandardList] = useState([]); // const [resourceStandardList, filterResourceStandard] = useComputingResource(); //
const [menuItems, setMenuItems] = useState([]); const [menuItems, setMenuItems] = useState([]);
useEffect(() => {
getComputingResource();
}, []);
//
const getComputingResource = async () => {
const params = {
page: 0,
size: 1000,
resource_type: '',
};
const [res] = await to(getComputingResourceReq(params));
if (res && res.data && res.data.content) {
setResourceStandardList(res.data.content);
}
};
const afterOpenChange = () => { const afterOpenChange = () => {
if (!open) { if (!open) {
console.log('zzzzz', form.getFieldsValue()); console.log('zzzzz', form.getFieldsValue());
@ -57,6 +43,7 @@ const Props = forwardRef(({ onParentChange }, ref) => {
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
getFieldsValue: async () => { getFieldsValue: async () => {
const [propsRes, propsError] = await to(form.validateFields()); const [propsRes, propsError] = await to(form.validateFields());
@ -155,18 +142,16 @@ const Props = forwardRef(({ onParentChange }, ref) => {
// icon // icon
const getSelectBtnIcon = (item) => { const getSelectBtnIcon = (item) => {
const type = item.item_type; const type = item.item_type;
let selectorType;
if (type === 'dataset') { if (type === 'dataset') {
return <KFIcon type="icon-xuanzeshujuji" />; selectorType = ResourceSelectorType.Dataset;
} else if (type === 'model') { } else if (type === 'model') {
return <KFIcon type="icon-xuanzemoxing" />; selectorType = ResourceSelectorType.Model;
} else { } else {
return <KFIcon type="icon-xuanzejingxiang" />; selectorType = ResourceSelectorType.Mirror;
} }
};
// return <KFIcon type={selectorTypeConfig[selectorType].buttonIcon} />;
const filterResourceStandard = (input, { computing_resource = '' }) => {
return computing_resource.toLocaleLowerCase().includes(input.toLocaleLowerCase());
}; };
// //

View File

@ -3,7 +3,7 @@
* @Date: 2024-03-25 13:52:54 * @Date: 2024-03-25 13:52:54
* @Description: https://umijs.org/docs/max/request * @Description: https://umijs.org/docs/max/request
*/ */
import type { RequestConfig } from '@umijs/max'; import type { AxiosRequestConfig, AxiosResponse, RequestConfig } from '@umijs/max';
import { message } from 'antd'; import { message } from 'antd';
import { clearSessionToken, getAccessToken } from './access'; import { clearSessionToken, getAccessToken } from './access';
import { setRemoteMenu } from './services/session'; import { setRemoteMenu } from './services/session';
@ -16,8 +16,8 @@ import { gotoLoginPage } from './utils/ui';
export const requestConfig: RequestConfig = { export const requestConfig: RequestConfig = {
errorConfig: {}, errorConfig: {},
requestInterceptors: [ requestInterceptors: [
(url: any, options: { headers: any }) => { (url: string, options: AxiosRequestConfig) => {
const headers = options.headers ? options.headers : []; const headers = options.headers ?? {};
const authHeader = headers['Authorization']; const authHeader = headers['Authorization'];
const isToken = headers['isToken']; const isToken = headers['isToken'];
if (!authHeader && isToken !== false) { if (!authHeader && isToken !== false) {
@ -30,7 +30,7 @@ export const requestConfig: RequestConfig = {
}, },
], ],
responseInterceptors: [ responseInterceptors: [
(response: any) => { (response: AxiosResponse) => {
const { status, data } = response || {}; const { status, data } = response || {};
if (status >= 200 && status < 300) { if (status >= 200 && status < 300) {
if (data && (data instanceof Blob || data.code === 200)) { if (data && (data instanceof Blob || data.code === 200)) {

View File

@ -14,6 +14,7 @@
@success-color: #1ace62; @success-color: #1ace62;
@error-color: #c73131; @error-color: #c73131;
@warning-color: #f98e1b; @warning-color: #f98e1b;
@abort-color: #8a8a8a;
@border-color: rgba(22, 100, 255, 0.3); @border-color: rgba(22, 100, 255, 0.3);
@border-color-secondary: rgba(22, 100, 255, 0.1); @border-color-secondary: rgba(22, 100, 255, 0.1);

View File

@ -29,7 +29,7 @@ export function parseJsonText(text?: string | null): any | null {
} }
} }
// Underscore-to-camelCase // underscore-to-camelCase
export function underscoreToCamelCase(obj: Record<string, any>) { export function underscoreToCamelCase(obj: Record<string, any>) {
const newObj: Record<string, any> = {}; const newObj: Record<string, any> = {};
for (const key in obj) { for (const key in obj) {
@ -47,6 +47,7 @@ export function underscoreToCamelCase(obj: Record<string, any>) {
return newObj; return newObj;
} }
// camelCase-to-underscore
export function camelCaseToUnderscore(obj: Record<string, any>) { export function camelCaseToUnderscore(obj: Record<string, any>) {
const newObj: Record<string, any> = {}; const newObj: Record<string, any> = {};
for (const key in obj) { for (const key in obj) {
@ -61,3 +62,21 @@ export function camelCaseToUnderscore(obj: Record<string, any>) {
} }
return newObj; return newObj;
} }
// null 转 undefined
export function nullToUndefined(obj: Record<string, any>) {
const newObj: Record<string, any> = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
if (value === null) {
newObj[key] = undefined;
} else if (typeof value === 'object' && value !== null) {
newObj[key] = nullToUndefined(value);
} else {
newObj[key] = value;
}
}
}
return newObj;
}

View File

@ -9,6 +9,8 @@ import zhCN from 'antd/locale/zh_CN';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { createRoot } from 'react-dom/client'; import { createRoot } from 'react-dom/client';
const destroyFns: (() => void)[] = [];
/** /**
* Function to open an Ant Design modal. * Function to open an Ant Design modal.
* *
@ -16,7 +18,6 @@ import { createRoot } from 'react-dom/client';
* @param modalProps - The modal properties. * @param modalProps - The modal properties.
* @return An object with a destroy method to close the modal. * @return An object with a destroy method to close the modal.
*/ */
export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>( export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
modal: (props: T) => React.ReactNode, modal: (props: T) => React.ReactNode,
modalProps: T, modalProps: T,
@ -29,6 +30,11 @@ export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
let timeoutId: ReturnType<typeof setTimeout>; let timeoutId: ReturnType<typeof setTimeout>;
function destroy() { function destroy() {
const index = destroyFns.indexOf(close);
if (index !== -1) {
destroyFns.splice(index, 1);
}
root.unmount(); root.unmount();
} }
@ -79,6 +85,8 @@ export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
render({ ...modalProps, open: true }); render({ ...modalProps, open: true });
destroyFns.push(close);
return { return {
close, close,
}; };
@ -88,19 +96,23 @@ export const openAntdModal = <T extends Omit<ModalProps, 'onOk'>>(
* Generates a custom hook for managing an Ant Design modal. * Generates a custom hook for managing an Ant Design modal.
* *
* @param modal - The function that renders the modal content. * @param modal - The function that renders the modal content.
* @param key - The key for the modal. * @param defaultProps - The default modal properties.
* @return The modal component, open function, and close function. * @return The modal component, open function, and close function.
*/ */
export const useAntdModal = <T extends ModalProps>(
export const useModal = <T extends ModalProps>(
modal: (props: T) => React.ReactNode, modal: (props: T) => React.ReactNode,
key: React.Key, defaultProps?: T,
) => { ) => {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [props, setProps] = useState<T>({} as T); const [props, setProps] = useState<T>(defaultProps || ({} as T));
const CustomModel = modal; const CustomModel = modal;
const open = (props: T) => { const open = (props: T) => {
setProps(props); setProps((prev) => ({
...prev,
...props,
}));
setVisible(true); setVisible(true);
}; };
@ -108,5 +120,14 @@ export const useAntdModal = <T extends ModalProps>(
setVisible(false); setVisible(false);
}; };
return [<CustomModel key={key} open={visible} {...props} />, open, close] as const; return [<CustomModel key="modal" open={visible} {...props} />, open, close] as const;
};
// 关闭没有手动关闭的 Modal
export const closeAllModals = () => {
let close = destroyFns.pop();
while (close) {
close();
close = destroyFns.pop();
}
}; };

View File

@ -7,6 +7,7 @@ import { PageEnum } from '@/enums/pagesEnums';
import themes from '@/styles/theme.less'; import themes from '@/styles/theme.less';
import { history } from '@umijs/max'; import { history } from '@umijs/max';
import { Modal, message, type ModalFuncProps, type UploadFile } from 'antd'; import { Modal, message, type ModalFuncProps, type UploadFile } from 'antd';
import { closeAllModals } from './modal';
// 自定义 Confirm 弹框 // 自定义 Confirm 弹框
export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) { export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) {
@ -58,6 +59,7 @@ export const gotoLoginPage = (toHome: boolean = true) => {
console.log('pathname', pathname); console.log('pathname', pathname);
console.log('search', search); console.log('search', search);
if (window.location.pathname !== PageEnum.LOGIN) { if (window.location.pathname !== PageEnum.LOGIN) {
closeAllModals();
history.replace({ history.replace({
pathname: PageEnum.LOGIN, pathname: PageEnum.LOGIN,
search: newSearch, search: newSearch,