mirror of
https://github.com/tencentmusic/cube-studio.git
synced 2025-02-17 14:40:28 +08:00
commit
60ebd9de01
@ -23,11 +23,11 @@ module.exports = {
|
||||
}),
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
config.plugins.push(
|
||||
new BundleAnalyzerPlugin()
|
||||
)
|
||||
}
|
||||
// if (process.env.NODE_ENV !== 'production') {
|
||||
// config.plugins.push(
|
||||
// new BundleAnalyzerPlugin()
|
||||
// )
|
||||
// }
|
||||
|
||||
return config;
|
||||
},
|
||||
|
@ -7,27 +7,43 @@ const job_template_modelview = (): Promise<any> => {
|
||||
return ajax.get('/job_template_modelview/api/');
|
||||
};
|
||||
|
||||
// 获取项目组
|
||||
const project_all = (): Promise<any> => {
|
||||
return ajax.get('/project_modelview/api/');
|
||||
};
|
||||
// 获取 org 项目组
|
||||
const project_modelview = (): Promise<any> => {
|
||||
return ajax.get('/project_modelview/org/api/');
|
||||
};
|
||||
|
||||
// 新增流水线
|
||||
|
||||
const pipeline_modelview_add = (data: IPipelineAdd): Promise<any> => {
|
||||
return ajax.post({ url: '/pipeline_modelview/api/', data });
|
||||
};
|
||||
|
||||
// 获取流水线列表
|
||||
const pipeline_modelview_demo = (): Promise<any> => {
|
||||
return ajax.get('/pipeline_modelview/demo/list/');
|
||||
};
|
||||
|
||||
// 获取流水线列表
|
||||
const pipeline_modelview_list = (): Promise<any> => {
|
||||
return ajax.get('/pipeline_modelview/my/list/');
|
||||
};
|
||||
|
||||
const pipeline_modelview_all = (filters: string): Promise<any> => {
|
||||
return ajax.get(`/pipeline_modelview/api/?form_data=${filters}`);
|
||||
};
|
||||
|
||||
// 获取流水线信息
|
||||
const pipeline_modelview_detail = (pipelineId: number | string): Promise<any> => {
|
||||
return ajax.get(`/pipeline_modelview/api/${pipelineId}`);
|
||||
};
|
||||
|
||||
// 删除指定流水线
|
||||
const pipeline_modelview_delete = (pipelineId: number | string): Promise<any> => {
|
||||
return ajax.delete(`/pipeline_modelview/api/${pipelineId}`);
|
||||
};
|
||||
|
||||
// 流水线编辑提交
|
||||
const pipeline_modelview_edit = (pipelineId: number | string, data: IPipelineEdit): Promise<any> => {
|
||||
return ajax.put({
|
||||
@ -96,10 +112,14 @@ const task_modelview_edit = (pipelineId: string | number, taskId: string | numbe
|
||||
|
||||
const api = {
|
||||
job_template_modelview,
|
||||
project_all,
|
||||
project_modelview,
|
||||
pipeline_modelview_add,
|
||||
pipeline_modelview_demo,
|
||||
pipeline_modelview_list,
|
||||
pipeline_modelview_all,
|
||||
pipeline_modelview_detail,
|
||||
pipeline_modelview_delete,
|
||||
pipeline_modelview_edit,
|
||||
pipeline_modelview_run,
|
||||
pipeline_modelview_copy,
|
||||
|
@ -14,7 +14,7 @@ const DataSet: React.FC<NodeProps> = props => {
|
||||
<div className={style.nodeConentTitleBar}>
|
||||
<div className={style.nodeIconWrapper}>
|
||||
<Icon iconName="Database" className={style.nodeIcon}></Icon>
|
||||
<div className={style.nodeTitle}>{props?.data?.name}</div>
|
||||
<div className={style.nodeTitle}>{props?.data?.label}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -151,10 +151,10 @@ const Model: React.FC<ModelProps> = props => {
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (taskChanged?.name) {
|
||||
if (taskChanged?.label) {
|
||||
const res = elements.map(ele => {
|
||||
if (ele.id === props.model.id) {
|
||||
const data = { ...ele.data, ...{ name: taskChanged.name } };
|
||||
const data = { ...ele.data, ...{ label: taskChanged.label } };
|
||||
return { ...ele, ...{ data } };
|
||||
}
|
||||
return ele;
|
||||
@ -223,24 +223,24 @@ const Model: React.FC<ModelProps> = props => {
|
||||
<div className={style.splitLine}></div>
|
||||
<TextField
|
||||
label="名称"
|
||||
description="英文名(字母、数字、- 组成),最长50个字符"
|
||||
onChange={(event: FormEvent, value?: string) => {
|
||||
handleOnChange('name', value ? value : '');
|
||||
}}
|
||||
value={task?.name || ''}
|
||||
disabled
|
||||
/>
|
||||
<div className={style.splitLine}></div>
|
||||
<TextField
|
||||
label="标签"
|
||||
description="中文名"
|
||||
description="节点标签"
|
||||
required
|
||||
onChange={(event: FormEvent, value?: string) => {
|
||||
handleOnChange('label', value ? value : '');
|
||||
}}
|
||||
value={task?.label || ''}
|
||||
/>
|
||||
{/* <div className={style.splitLine}></div>
|
||||
<TextField
|
||||
<div className={style.splitLine}></div>
|
||||
{/* <TextField
|
||||
label="挂载目录"
|
||||
description="外部挂载,格式:$pvc_name1(pvc):/$container_path1,$hostpath1(hostpath):/$container_path2,注意pvc会自动挂载对应目录下的个人rtx子目录"
|
||||
onChange={(event: FormEvent, value?: string) => {
|
||||
@ -276,8 +276,8 @@ const Model: React.FC<ModelProps> = props => {
|
||||
handleOnChange('node_selector', value ? value : '');
|
||||
}}
|
||||
value={task?.node_selector || ''}
|
||||
/> */}
|
||||
<div className={style.splitLine}></div>
|
||||
/>
|
||||
<div className={style.splitLine}></div> */}
|
||||
<TextField
|
||||
label="内存申请"
|
||||
description="内存的资源使用限制,示例1G,10G, 最大10G,如需更多联系管理员"
|
||||
|
@ -142,6 +142,7 @@ const Setting: React.FC = () => {
|
||||
handleOnChange('name', value ? value : '');
|
||||
}}
|
||||
value={current?.name || ''}
|
||||
disabled
|
||||
/>
|
||||
<div className={style.splitLine}></div>
|
||||
<TextField
|
||||
@ -211,7 +212,7 @@ const Setting: React.FC = () => {
|
||||
]}
|
||||
selectedKey={`${current.depends_on_past}`}
|
||||
onChange={(event: FormEvent, option?: IDropdownOption) => {
|
||||
handleOnChange('depends_on_past', option?.data || true);
|
||||
handleOnChange('depends_on_past', option?.data);
|
||||
}}
|
||||
/>
|
||||
<div className={style.splitLine}></div>
|
||||
|
@ -61,15 +61,15 @@ const EditorBody: React.FC = () => {
|
||||
if (pipelineId) {
|
||||
dispatch(updateLoading(true));
|
||||
// http://kubeflow.music.woa.com/job_template_modelview/api/_info
|
||||
const taskName = `${modelInfo.name
|
||||
.replace(/\.|[\u4e00-\u9fa5]/g, '')
|
||||
.replace(/_|\s/g, '-')}-${Date.now()}`.substring(0, 49);
|
||||
const taskName = `${
|
||||
modelInfo.name.replace(/\.|[\u4e00-\u9fa5]/g, '').replace(/_|\s/g, '-') || 'task'
|
||||
}-${Date.now()}`.substring(0, 49);
|
||||
api
|
||||
.task_modelview_add(pipelineId, {
|
||||
job_template: modelInfo.id,
|
||||
pipeline: +pipelineId,
|
||||
name: taskName,
|
||||
label: `新建 ${modelInfo.name} task`,
|
||||
label: `新建 ${modelInfo.name} 任务`,
|
||||
volume_mount: 'kubeflow-user-workspace(pvc):/mnt,kubeflow-archives(pvc):/archives',
|
||||
image_pull_policy: 'Always',
|
||||
working_dir: '',
|
||||
@ -90,6 +90,7 @@ const EditorBody: React.FC = () => {
|
||||
position,
|
||||
data: {
|
||||
name: taskName,
|
||||
label: `新建 ${modelInfo.name} 任务`,
|
||||
},
|
||||
};
|
||||
dispatch(updateEditing(true));
|
||||
@ -166,6 +167,9 @@ const EditorBody: React.FC = () => {
|
||||
if (isEdge(ele) && !ele?.arrowHeadType) {
|
||||
return Object.assign(ele, { arrowHeadType: 'arrow' });
|
||||
}
|
||||
if (isNode(ele) && !ele.data.label) {
|
||||
ele.data.label = ele.data.name;
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import { Stack, TooltipHost, IconButton, PrimaryButton, DefaultButton } from '@fluentui/react';
|
||||
import moment from 'moment';
|
||||
import ErrorTips from '../ErrorTips';
|
||||
import { useAppSelector, useAppDispatch } from '../../../../models/hooks';
|
||||
import { updateErrMsg } from '../../../../models/app';
|
||||
import { selectPipelineId, selectInfo, savePipeline } from '../../../../models/pipeline';
|
||||
import { saveTaskList } from '../../../../models/task';
|
||||
import { toggle } from '../../../../models/setting';
|
||||
import api from '../../../../api';
|
||||
import { useAppSelector, useAppDispatch } from '@src/models/hooks';
|
||||
import { updateErrMsg, selectUserName } from '@src/models/app';
|
||||
import { selectPipelineId, selectInfo, savePipeline } from '@src/models/pipeline';
|
||||
import { saveTaskList } from '@src/models/task';
|
||||
import { toggle } from '@src/models/setting';
|
||||
import api from '@src/api';
|
||||
import style from './style';
|
||||
const { Item } = Stack;
|
||||
|
||||
@ -15,13 +14,14 @@ const EditorHead: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const pipelineId = useAppSelector(selectPipelineId);
|
||||
const info = useAppSelector(selectInfo);
|
||||
const userName = useAppSelector(selectUserName);
|
||||
|
||||
// 新建流水线
|
||||
const handleNewPipeline = () => {
|
||||
api
|
||||
.pipeline_modelview_add({
|
||||
describe: '新建流水线',
|
||||
name: `pipeline-created-on-${moment().format('MM-DD-HHmmSSSS')}`,
|
||||
describe: `新建流水线-${Date.now()}`,
|
||||
name: `${userName}-pipeline-${Date.now()}`,
|
||||
node_selector: 'cpu=true,train=true',
|
||||
schedule_type: 'once',
|
||||
image_pull_policy: 'Always',
|
||||
@ -56,7 +56,7 @@ const EditorHead: React.FC = () => {
|
||||
<Stack horizontal>
|
||||
<Item>
|
||||
{info.name ? (
|
||||
<div className={style.headNameStyle}>{info.name}</div>
|
||||
<div className={style.headNameStyle}>{info.describe}</div>
|
||||
) : (
|
||||
<PrimaryButton
|
||||
styles={{
|
||||
|
@ -149,6 +149,21 @@ const EditorTool: React.FC = () => {
|
||||
dispatch(updateElements(removeElements(selectedElements, elements)));
|
||||
},
|
||||
},
|
||||
{
|
||||
buttonStyles: style.commonButton,
|
||||
iconOnly: true,
|
||||
key: 'monitor',
|
||||
iconProps: {
|
||||
iconName: 'NetworkTower',
|
||||
styles: style.commonIcon,
|
||||
},
|
||||
text: '监控',
|
||||
onClick: () => {
|
||||
if (pipeline?.id) {
|
||||
window.open(`${window.location.origin}/pipeline_modelview/web/monitoring/${pipelineId}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// task 发生编辑行为时状态变更
|
||||
|
9
myapp/vision/src/components/Home/Pipeline/index.tsx
Normal file
9
myapp/vision/src/components/Home/Pipeline/index.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { Stack } from '@fluentui/react';
|
||||
import style from './style';
|
||||
|
||||
const Pipeline: React.FC = () => {
|
||||
return <Stack className={style.pipelineList}></Stack>;
|
||||
};
|
||||
|
||||
export default Pipeline;
|
8
myapp/vision/src/components/Home/Pipeline/style.ts
Normal file
8
myapp/vision/src/components/Home/Pipeline/style.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { mergeStyles } from '@fluentui/merge-styles';
|
||||
|
||||
const pipelineList = mergeStyles({
|
||||
marginTop: '16px !important',
|
||||
padding: '0 10px 24px',
|
||||
});
|
||||
|
||||
export default { pipelineList };
|
@ -1,9 +1,8 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Icon, Stack, Modal } from '@fluentui/react';
|
||||
import api from '@src/api';
|
||||
import { updateErrMsg } from '@src/models/app';
|
||||
import { useAppDispatch } from '@src/models/hooks';
|
||||
import moment from 'moment';
|
||||
import { updateErrMsg, selectUserName } from '@src/models/app';
|
||||
import { useAppDispatch, useAppSelector } from '@src/models/hooks';
|
||||
import Player from 'xgplayer';
|
||||
import style from './style';
|
||||
|
||||
@ -19,12 +18,13 @@ const Section: React.FC<ISectionProps> = props => {
|
||||
const [showModal, setShowModal] = useState<boolean>(false);
|
||||
const [curVideo, setCurVideo] = useState<string>('');
|
||||
const [curPlayer, setCurPlayer] = useState<Player | undefined>(undefined);
|
||||
const userName = useAppSelector(selectUserName);
|
||||
|
||||
const handleNewPipeline = () => {
|
||||
api
|
||||
.pipeline_modelview_add({
|
||||
describe: '新建流水线',
|
||||
name: `pipeline-created-on-${moment().format('MM-DD-HHmmSSSS')}`,
|
||||
describe: `new-pipeline-${Date.now()}`,
|
||||
name: `${userName}-pipeline-${Date.now()}`,
|
||||
node_selector: 'cpu=true,train=true',
|
||||
schedule_type: 'once',
|
||||
image_pull_policy: 'Always',
|
||||
@ -58,7 +58,19 @@ const Section: React.FC<ISectionProps> = props => {
|
||||
const handleClick = (item: any) => {
|
||||
switch (item.type) {
|
||||
case 'link':
|
||||
window.location.href = item.url;
|
||||
if (window.self === window.top) {
|
||||
window.location.href = `${window.location.origin}${location.pathname}?pipeline_id=${item?.id}`;
|
||||
} else {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: 'link',
|
||||
message: {
|
||||
pipelineId: item?.id,
|
||||
},
|
||||
},
|
||||
`${window.location.origin}`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'outside':
|
||||
window.open(item.url, '_blank');
|
||||
@ -129,6 +141,7 @@ const Section: React.FC<ISectionProps> = props => {
|
||||
) : null}
|
||||
{/* sample card item */}
|
||||
{props.data.map((item: any, key: number) => {
|
||||
if (!item.img) return null;
|
||||
return (
|
||||
<div
|
||||
className={style.sampleCardStyle}
|
||||
|
@ -6,11 +6,12 @@ const sectionStyles = mergeStyles({
|
||||
borderBottom: '1px solid #dadada',
|
||||
selectors: {
|
||||
'.subtitle': {
|
||||
marginBottom: 8,
|
||||
marginBottom: 20,
|
||||
height: 24,
|
||||
lineHeight: '22px',
|
||||
fontSize: 16,
|
||||
fontWeight: 600,
|
||||
lineHeight: '1.1',
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: '"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif;',
|
||||
},
|
||||
'.expand-button': {
|
||||
color: '#005cd2',
|
||||
@ -76,7 +77,6 @@ const sampleImgStyles = mergeStyles({
|
||||
});
|
||||
|
||||
const cardTitleStyles = mergeStyles({
|
||||
lineHeight: '20px',
|
||||
marginTop: 16,
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
|
@ -1,12 +1,17 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import cookie from 'cookie';
|
||||
import { RootState } from '../store';
|
||||
|
||||
const { myapp_username, t_uid, km_uid } = cookie.parse(document.cookie);
|
||||
|
||||
export interface AppState {
|
||||
errMsg: any;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
const initialState: AppState = {
|
||||
errMsg: null,
|
||||
userName: myapp_username || t_uid || km_uid,
|
||||
};
|
||||
|
||||
const AppSlice = createSlice({
|
||||
@ -22,5 +27,6 @@ const AppSlice = createSlice({
|
||||
export const { updateErrMsg } = AppSlice.actions;
|
||||
|
||||
export const selectErrMsg = (state: RootState): AppState['errMsg'] => state.app.errMsg;
|
||||
export const selectUserName = (state: RootState): AppState['userName'] => state.app.userName;
|
||||
|
||||
export default AppSlice.reducer;
|
||||
|
@ -12,6 +12,7 @@ export interface PipelineState {
|
||||
changed: any;
|
||||
editing: boolean;
|
||||
pipelineList: any[];
|
||||
all: any[] | undefined;
|
||||
}
|
||||
|
||||
const initialState: PipelineState = {
|
||||
@ -21,6 +22,7 @@ const initialState: PipelineState = {
|
||||
changed: {},
|
||||
editing: false,
|
||||
pipelineList: [],
|
||||
all: undefined,
|
||||
};
|
||||
|
||||
const pipelineSlice = createSlice({
|
||||
@ -45,11 +47,21 @@ const pipelineSlice = createSlice({
|
||||
updatePipelineList: (state, action: PayloadAction<any[]>) => {
|
||||
state.pipelineList = action.payload;
|
||||
},
|
||||
updateAll: (state, action: PayloadAction<any[] | undefined>) => {
|
||||
state.all = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { updatePipelineId, updateInfo, updateSaved, updateChanged, updateEditing, updatePipelineList } =
|
||||
pipelineSlice.actions;
|
||||
export const {
|
||||
updatePipelineId,
|
||||
updateInfo,
|
||||
updateSaved,
|
||||
updateChanged,
|
||||
updateEditing,
|
||||
updatePipelineList,
|
||||
updateAll,
|
||||
} = pipelineSlice.actions;
|
||||
|
||||
export const selectPipelineId = (state: RootState): PipelineState['pipelineId'] => state.pipeline.pipelineId;
|
||||
export const selectInfo = (state: RootState): PipelineState['info'] => state.pipeline.info;
|
||||
@ -57,23 +69,58 @@ export const selectSaved = (state: RootState): PipelineState['saved'] => state.p
|
||||
export const selectChanged = (state: RootState): PipelineState['changed'] => state.pipeline.changed;
|
||||
export const selectEditing = (state: RootState): PipelineState['editing'] => state.pipeline.editing;
|
||||
export const selectPipelineList = (state: RootState): PipelineState['pipelineList'] => state.pipeline.pipelineList;
|
||||
export const selectAll = (state: RootState): PipelineState['all'] => state.pipeline.all;
|
||||
|
||||
export const getPipelineList = (): AppThunk => dispatch => {
|
||||
api.pipeline_modelview_list().then(res => {
|
||||
if (res?.status === 0) {
|
||||
const list = res.result.map((item: any) => {
|
||||
const list: any[] = res.result.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
describe: item.describe,
|
||||
changed_on: item.changed_on,
|
||||
project_id: item.project_id,
|
||||
};
|
||||
});
|
||||
dispatch(updatePipelineList(list));
|
||||
dispatch(
|
||||
updatePipelineList(list.sort((a, b) => new Date(b.changed_on).getTime() - new Date(a.changed_on).getTime())),
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getAllList =
|
||||
(data: unknown): AppThunk =>
|
||||
dispatch => {
|
||||
dispatch(updateAll(undefined));
|
||||
api
|
||||
.pipeline_modelview_all(JSON.stringify(data))
|
||||
.then(res => {
|
||||
if (res?.status === 0) {
|
||||
const list: any[] = res.result.map((item: any) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
describe: item.describe,
|
||||
changed_on: item.changed_on,
|
||||
project_id: item.project.id,
|
||||
};
|
||||
});
|
||||
dispatch(updateAll(list.sort((a, b) => new Date(b.changed_on).getTime() - new Date(a.changed_on).getTime())));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.response) {
|
||||
dispatch(
|
||||
updateErrMsg({
|
||||
msg: err?.response?.data?.message || '获取列表失败',
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取当前流水线信息
|
||||
export const getPipeline = (): AppThunk => (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
@ -1,9 +1,28 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { DetailsList, DetailsListLayoutMode, IStackStyles, SelectionMode, Stack, IColumn } from '@fluentui/react';
|
||||
import {
|
||||
DetailsList,
|
||||
DetailsListLayoutMode,
|
||||
ShimmeredDetailsList,
|
||||
IStackStyles,
|
||||
SelectionMode,
|
||||
Stack,
|
||||
IColumn,
|
||||
TooltipHost,
|
||||
IconButton,
|
||||
PrimaryButton,
|
||||
Pivot,
|
||||
PivotItem,
|
||||
Dropdown,
|
||||
IDropdownOption,
|
||||
IDropdownStyles,
|
||||
Text,
|
||||
} from '@fluentui/react';
|
||||
import api from '@src/api';
|
||||
import Section from '@src/components/Home/Section';
|
||||
import { pipelineDemo, videoDemo } from '@src/static/home';
|
||||
import { videoDemo } from '@src/static/home';
|
||||
import { useAppDispatch, useAppSelector } from '@src/models/hooks';
|
||||
import { getPipelineList, selectPipelineList } from '@src/models/pipeline';
|
||||
import { getPipelineList, selectPipelineList, selectAll, getAllList } from '@src/models/pipeline';
|
||||
|
||||
const homeContainerStyle: IStackStyles = {
|
||||
root: {
|
||||
@ -13,11 +32,39 @@ const homeContainerStyle: IStackStyles = {
|
||||
},
|
||||
};
|
||||
|
||||
const dropdownStyles: Partial<IDropdownStyles> = {
|
||||
dropdown: { width: 100, marginLeft: 10, marginRight: 20 },
|
||||
};
|
||||
const options: IDropdownOption[] = [
|
||||
{
|
||||
key: '10',
|
||||
text: '10',
|
||||
},
|
||||
{
|
||||
key: '25',
|
||||
text: '25',
|
||||
},
|
||||
{
|
||||
key: '50',
|
||||
text: '50',
|
||||
},
|
||||
{
|
||||
key: '100',
|
||||
text: '100',
|
||||
},
|
||||
];
|
||||
|
||||
const Home: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const myPipeLine = useAppSelector(selectPipelineList);
|
||||
const myAll = useAppSelector(selectAll);
|
||||
const [videoList, setVideoList] = useState<any>([]);
|
||||
const [pipelineList, setPipelineList] = useState<any>([]);
|
||||
const [projectList, setProjectList] = useState<any>([]);
|
||||
const [page, setPage] = useState<number>(0);
|
||||
const [pageSize, setPageSize] = useState<number>(10);
|
||||
const [hasProject, setHasProject] = useState<boolean>(false);
|
||||
const [isEnd, setIsEnd] = useState<boolean>(false);
|
||||
|
||||
const column: IColumn[] = [
|
||||
{
|
||||
@ -35,7 +82,6 @@ const Home: React.FC = () => {
|
||||
minWidth: 200,
|
||||
maxWidth: 350,
|
||||
data: 'string',
|
||||
// eslint-disable-next-line react/display-name
|
||||
onRender: (item: any) => (
|
||||
<span
|
||||
style={{
|
||||
@ -67,8 +113,18 @@ const Home: React.FC = () => {
|
||||
maxWidth: 300,
|
||||
data: 'string',
|
||||
},
|
||||
{
|
||||
key: 'project_id',
|
||||
name: '项目组',
|
||||
minWidth: 150,
|
||||
maxWidth: 200,
|
||||
onRender: (item: any) => {
|
||||
return <div>{projectList[item.project_id].name}</div>;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// 跳转指定pipeline
|
||||
const goPipeline = (item: any) => {
|
||||
if (window.self === window.top) {
|
||||
window.location.href = `${window.location.origin}${location.pathname}?pipeline_id=${item?.id}`;
|
||||
@ -85,12 +141,68 @@ const Home: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 删除指定pipeline
|
||||
const deletePipeline = (item: any) => {
|
||||
if (item?.id) {
|
||||
api.pipeline_modelview_delete(item.id).then((res: any) => {
|
||||
if (res.status === 0) {
|
||||
dispatch(getPipelineList());
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
useEffect(() => {
|
||||
setVideoList(videoDemo);
|
||||
setPipelineList(pipelineDemo);
|
||||
dispatch(getPipelineList());
|
||||
api.pipeline_modelview_demo().then((res: any) => {
|
||||
if (res.status === 0) {
|
||||
const { result } = res;
|
||||
const pipelineDemo = result.map((ele: any) => {
|
||||
const cur = {
|
||||
id: ele.id,
|
||||
name: ele.describe,
|
||||
img: JSON.parse(ele.parameter).img || '',
|
||||
type: 'link',
|
||||
};
|
||||
return cur;
|
||||
});
|
||||
|
||||
setPipelineList(pipelineDemo);
|
||||
}
|
||||
});
|
||||
api.project_all().then((res: any) => {
|
||||
if (res.status === 0) {
|
||||
setHasProject(true);
|
||||
const list: any = [];
|
||||
res.result.forEach((ele: any) => {
|
||||
if (ele.id) {
|
||||
list[ele.id] = ele;
|
||||
}
|
||||
});
|
||||
setProjectList(list);
|
||||
dispatch(getPipelineList());
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasProject) {
|
||||
dispatch(
|
||||
getAllList({
|
||||
page,
|
||||
page_size: pageSize,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [pageSize, hasProject, page]);
|
||||
|
||||
useEffect(() => {
|
||||
if (myAll) {
|
||||
setIsEnd(myAll.length < pageSize);
|
||||
}
|
||||
}, [myAll]);
|
||||
|
||||
return (
|
||||
<Stack className="home-container" styles={homeContainerStyle}>
|
||||
<Stack
|
||||
@ -109,7 +221,6 @@ const Home: React.FC = () => {
|
||||
<Stack
|
||||
styles={{
|
||||
root: {
|
||||
width: '50%',
|
||||
marginTop: '16px !important',
|
||||
padding: '0 10px 24px',
|
||||
},
|
||||
@ -121,22 +232,149 @@ const Home: React.FC = () => {
|
||||
style={{
|
||||
marginBottom: 8,
|
||||
height: 24,
|
||||
lineHeight: '22px',
|
||||
fontSize: 16,
|
||||
fontWeight: 600,
|
||||
lineHeight: '1.1',
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
我的流水线
|
||||
流水线
|
||||
</div>
|
||||
</Stack>
|
||||
<DetailsList
|
||||
items={myPipeLine}
|
||||
columns={column}
|
||||
selectionMode={SelectionMode.none}
|
||||
setKey="none"
|
||||
layoutMode={DetailsListLayoutMode.justified}
|
||||
isHeaderVisible={true}
|
||||
/>
|
||||
<Pivot aria-label="Basic Pivot Example" defaultSelectedKey="1">
|
||||
<PivotItem
|
||||
headerText="我的"
|
||||
headerButtonProps={{
|
||||
'data-order': 1,
|
||||
'data-title': 'My Files Title',
|
||||
}}
|
||||
itemKey="1"
|
||||
>
|
||||
<div>
|
||||
<DetailsList
|
||||
items={myPipeLine}
|
||||
columns={column.concat({
|
||||
key: 'action',
|
||||
name: '操作',
|
||||
minWidth: 200,
|
||||
maxWidth: 300,
|
||||
onRender: (item: any) => {
|
||||
return (
|
||||
<div>
|
||||
<TooltipHost content="删除">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
deletePipeline(item);
|
||||
}}
|
||||
iconProps={{
|
||||
iconName: 'Delete',
|
||||
styles: {
|
||||
root: {
|
||||
color: 'red',
|
||||
},
|
||||
},
|
||||
}}
|
||||
></IconButton>
|
||||
</TooltipHost>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
})}
|
||||
selectionMode={SelectionMode.none}
|
||||
setKey="none"
|
||||
layoutMode={DetailsListLayoutMode.fixedColumns}
|
||||
isHeaderVisible={true}
|
||||
compact={true}
|
||||
styles={{
|
||||
headerWrapper: {
|
||||
'.ms-DetailsHeader': {
|
||||
paddingTop: 0,
|
||||
},
|
||||
},
|
||||
contentWrapper: {
|
||||
lineHeight: '32px',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</PivotItem>
|
||||
<PivotItem headerText="协作" itemKey="2">
|
||||
<div>
|
||||
<ShimmeredDetailsList
|
||||
setKey="none"
|
||||
isHeaderVisible={true}
|
||||
items={myAll || []}
|
||||
columns={column}
|
||||
compact={!!myAll}
|
||||
selectionMode={SelectionMode.none}
|
||||
layoutMode={DetailsListLayoutMode.fixedColumns}
|
||||
enableShimmer={!myAll}
|
||||
detailsListStyles={{
|
||||
headerWrapper: {
|
||||
'.ms-DetailsHeader': {
|
||||
paddingTop: 0,
|
||||
},
|
||||
},
|
||||
contentWrapper: {
|
||||
lineHeight: '32px',
|
||||
},
|
||||
}}
|
||||
listProps={{
|
||||
renderedWindowsAhead: 0,
|
||||
renderedWindowsBehind: 0,
|
||||
}}
|
||||
></ShimmeredDetailsList>
|
||||
<Stack
|
||||
horizontal
|
||||
reversed
|
||||
verticalAlign="center"
|
||||
styles={{
|
||||
root: {
|
||||
marginTop: 20,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<PrimaryButton
|
||||
text="下一页"
|
||||
styles={{ root: { marginRight: 10 } }}
|
||||
disabled={isEnd}
|
||||
onClick={() => {
|
||||
if (isEnd || !myAll) return;
|
||||
setPage(page + 1);
|
||||
}}
|
||||
></PrimaryButton>
|
||||
<PrimaryButton
|
||||
text="上一页"
|
||||
styles={{ root: { marginRight: 10 } }}
|
||||
disabled={page === 0}
|
||||
onClick={() => {
|
||||
if (page === 0 || !myAll) return;
|
||||
setPage(page - 1);
|
||||
}}
|
||||
></PrimaryButton>
|
||||
<Dropdown
|
||||
defaultSelectedKey={'10'}
|
||||
placeholder="选择页数"
|
||||
options={options}
|
||||
styles={dropdownStyles}
|
||||
onChange={(e, opt) => {
|
||||
setPage(0);
|
||||
opt?.key && setPageSize(+opt.key);
|
||||
}}
|
||||
/>
|
||||
<Text
|
||||
styles={{
|
||||
root: {
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
},
|
||||
}}
|
||||
>
|
||||
选择页数
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
@ -1,89 +1,8 @@
|
||||
export const pipelineDemo = [
|
||||
{
|
||||
name: 'nni超参搜索',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/nni.png',
|
||||
url: 'http://kubeflow.tke.woa.com/nni_modelview/list/',
|
||||
type: 'link',
|
||||
},
|
||||
{
|
||||
name: '搭建私有集群',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/private.png',
|
||||
url: 'http://tapd.oa.com/kubeflow/markdown_wikis/show/#1220424693001918725',
|
||||
type: 'outside',
|
||||
},
|
||||
{
|
||||
name: '创建在线vscode',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/vscode.png',
|
||||
url: 'http://kubeflow.tke.woa.com/notebook_modelview/add',
|
||||
type: 'link',
|
||||
},
|
||||
{
|
||||
name: '问答搜索中心',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/search.png',
|
||||
url: 'http://kubeflow.service.kfserving.woa.com/',
|
||||
type: 'outside',
|
||||
},
|
||||
{
|
||||
name: '图片视频卡通化',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/cart.png',
|
||||
url: 'http://cartoonizer.service.kfserving.woa.com/',
|
||||
type: 'outside',
|
||||
},
|
||||
{
|
||||
name: '聊天机器人',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/chatbot.jpg',
|
||||
url: 'http://chatbot.service.kfserving.woa.com/',
|
||||
type: 'outside',
|
||||
},
|
||||
{
|
||||
name: '人脸AI特效',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/face.jpg',
|
||||
url: 'https://face-master.service.kfserving.woa.com/',
|
||||
type: 'outside',
|
||||
},
|
||||
{
|
||||
name: '人体姿态识别',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/openpose.jpg',
|
||||
url: 'https://openpose-gpu.service.kfserving.woa.com/',
|
||||
type: 'outside',
|
||||
},
|
||||
{
|
||||
name: '机器学习pipeline流水线',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/dag.jpg',
|
||||
url: 'http://kubeflow.tke.woa.com/pipeline_modelview/list/',
|
||||
type: 'link',
|
||||
},
|
||||
{
|
||||
name: '在线ide编辑器',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/ide.png',
|
||||
url: 'http://kubeflow.tke.woa.com/notebook_modelview/list/',
|
||||
type: 'link',
|
||||
},
|
||||
{
|
||||
name: '超参搜索',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/automl.png',
|
||||
url: 'http://kubeflow.tke.woa.com/hyperparameter_tuning_modelview/list/',
|
||||
type: 'link',
|
||||
},
|
||||
{
|
||||
name: '模型服务化',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/home/service.png',
|
||||
url: 'http://kubeflow.tke.woa.com/service_modelview/list/',
|
||||
type: 'link',
|
||||
},
|
||||
];
|
||||
|
||||
export const videoDemo = [
|
||||
{
|
||||
name: '新人制作一个pipeline',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/ad/video-cover2-thumb.png',
|
||||
url: 'http://kubeflow.tke.woa.com/static/appbuilder/mnt/make_pipeline.mp4',
|
||||
type: 'video',
|
||||
},
|
||||
{
|
||||
name: '开发定制一个任务模板',
|
||||
img: 'http://kubeflow.tke.woa.com/static/assets/images/ad/video-cover2-thumb.png',
|
||||
url: 'http://kubeflow.tke.woa.com/static/appbuilder/mnt/make_job_template.mp4',
|
||||
name: '',
|
||||
img: '',
|
||||
url: '',
|
||||
type: 'video',
|
||||
},
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user