| @@ -49,7 +49,10 @@ export function useComputingResource() { | |||
| // 根据 standard 获取 description | |||
| const getDescription = useCallback( | |||
| (standard: string) => { | |||
| (standard?: string) => { | |||
| if (!standard) { | |||
| return undefined; | |||
| } | |||
| return resourceStandardList.find((item) => item.standard === standard)?.description; | |||
| }, | |||
| [resourceStandardList], | |||
| @@ -3,7 +3,7 @@ import { loginByOauth2Req } from '@/services/auth'; | |||
| import { to } from '@/utils/promise'; | |||
| import { history, useModel, useSearchParams } from '@umijs/max'; | |||
| import { message } from 'antd'; | |||
| import { useEffect } from 'react'; | |||
| import { useCallback, useEffect } from 'react'; | |||
| import { flushSync } from 'react-dom'; | |||
| import styles from './index.less'; | |||
| @@ -12,12 +12,21 @@ function Authorize() { | |||
| const [searchParams] = useSearchParams(); | |||
| const code = searchParams.get('code'); | |||
| const redirect = searchParams.get('redirect'); | |||
| useEffect(() => { | |||
| loginByOauth2(); | |||
| }, []); | |||
| const fetchUserInfo = useCallback(async () => { | |||
| const userInfo = await initialState?.fetchUserInfo?.(); | |||
| if (userInfo) { | |||
| flushSync(() => { | |||
| setInitialState((s) => ({ | |||
| ...s, | |||
| currentUser: userInfo, | |||
| })); | |||
| }); | |||
| } | |||
| }, [initialState, setInitialState]); | |||
| // 登录 | |||
| const loginByOauth2 = async () => { | |||
| const loginByOauth2 = useCallback(async () => { | |||
| const params = { | |||
| code, | |||
| }; | |||
| @@ -29,19 +38,11 @@ function Authorize() { | |||
| await fetchUserInfo(); | |||
| history.push(redirect || '/'); | |||
| } | |||
| }; | |||
| }, [fetchUserInfo, redirect, code]); | |||
| const fetchUserInfo = async () => { | |||
| const userInfo = await initialState?.fetchUserInfo?.(); | |||
| if (userInfo) { | |||
| flushSync(() => { | |||
| setInitialState((s) => ({ | |||
| ...s, | |||
| currentUser: userInfo, | |||
| })); | |||
| }); | |||
| } | |||
| }; | |||
| useEffect(() => { | |||
| loginByOauth2(); | |||
| }, [loginByOauth2]); | |||
| return <div className={styles.container}></div>; | |||
| } | |||
| @@ -39,6 +39,7 @@ function AutoMLInstance() { | |||
| return () => { | |||
| closeSSE(); | |||
| }; | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []); | |||
| // 获取实验实例详情 | |||
| @@ -83,9 +84,6 @@ function AutoMLInstance() { | |||
| const setupSSE = (name: string, namespace: string) => { | |||
| const { origin } = location; | |||
| // if (process.env.NODE_ENV === 'development') { | |||
| // origin = 'http://172.20.32.197:31213'; | |||
| // } | |||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | |||
| const evtSource = new EventSource( | |||
| `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`, | |||
| @@ -21,7 +21,6 @@ function ExperimentText() { | |||
| const [experimentIns, setExperimentIns] = useState(undefined); | |||
| const [experimentNodeData, setExperimentNodeData, experimentNodeDataRef] = useStateRef(undefined); | |||
| const graphRef = useRef(); | |||
| const timerRef = useRef(); | |||
| const workflowRef = useRef(); | |||
| const locationParams = useParams(); // 新版本获取路由参数接口 | |||
| const [paramsModalOpen, openParamsModal, closeParamsModal] = useVisible(false); | |||
| @@ -36,6 +35,16 @@ function ExperimentText() { | |||
| initGraph(); | |||
| getWorkflow(); | |||
| return () => { | |||
| if (evtSourceRef.current) { | |||
| evtSourceRef.current.close(); | |||
| evtSourceRef.current = null; | |||
| } | |||
| }; | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []); | |||
| useEffect(() => { | |||
| const changeSize = () => { | |||
| if (!graph || graph.get('destroyed')) return; | |||
| if (!graphRef.current) return; | |||
| @@ -46,13 +55,6 @@ function ExperimentText() { | |||
| window.addEventListener('resize', changeSize); | |||
| return () => { | |||
| window.removeEventListener('resize', changeSize); | |||
| if (timerRef.current) { | |||
| clearTimeout(timerRef.current); | |||
| } | |||
| if (evtSourceRef.current) { | |||
| evtSourceRef.current.close(); | |||
| evtSourceRef.current = null; | |||
| } | |||
| }; | |||
| }, []); | |||
| @@ -46,12 +46,94 @@ function LogGroup({ | |||
| setHasRun(true); | |||
| } | |||
| // 进入页面时,滚动到底部 | |||
| useEffect(() => { | |||
| scrollToBottom(false); | |||
| }, []); | |||
| useEffect(() => { | |||
| // 建立 socket 连接 | |||
| const setupSockect = () => { | |||
| let { host } = location; | |||
| if (process.env.NODE_ENV === 'development') { | |||
| host = '172.20.32.197:31213'; | |||
| } | |||
| const socket = new WebSocket( | |||
| `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`, | |||
| ); | |||
| socket.addEventListener('open', () => { | |||
| console.log('WebSocket is open now.'); | |||
| }); | |||
| socket.addEventListener('close', (event) => { | |||
| console.log('WebSocket is closed:', event); | |||
| // 有时候会出现连接失败,重试 3 次 | |||
| if (event.code !== 1000 && retryRef.current > 0) { | |||
| retryRef.current -= 1; | |||
| setTimeout(() => { | |||
| setupSockect(); | |||
| }, 2 * 1000); | |||
| } | |||
| }); | |||
| socket.addEventListener('error', (event) => { | |||
| console.error('WebSocket error observed:', event); | |||
| }); | |||
| socket.addEventListener('message', (event) => { | |||
| // console.log('message received.', event); | |||
| if (!event.data) { | |||
| return; | |||
| } | |||
| try { | |||
| const data = JSON.parse(event.data); | |||
| const streams = data.streams; | |||
| if (!streams || !Array.isArray(streams)) { | |||
| return; | |||
| } | |||
| let startTime = start_time; | |||
| const logContent = streams.reduce((result, item) => { | |||
| const values = item.values; | |||
| return ( | |||
| result + | |||
| values.reduce((prev: string, cur: [string, string]) => { | |||
| const [time, value] = cur; | |||
| startTime = time; | |||
| const str = `[${dayjs(Number(time) / 1.0e6).format( | |||
| 'YYYY-MM-DD HH:mm:ss', | |||
| )}] ${value}`; | |||
| return prev + str; | |||
| }, '') | |||
| ); | |||
| }, ''); | |||
| const logDetail: Log = { | |||
| start_time: startTime!, | |||
| log_content: logContent, | |||
| pod_name: pod_name, | |||
| }; | |||
| setLogList((oldList) => oldList.concat(logDetail)); | |||
| if (!isMouseDownRef.current && logContent) { | |||
| setTimeout(() => { | |||
| scrollToBottom(); | |||
| }, 100); | |||
| } | |||
| } catch (error) { | |||
| console.error('JSON parse error: ', error); | |||
| } | |||
| }); | |||
| socketRef.current = socket; | |||
| }; | |||
| if (status === ExperimentStatus.Running) { | |||
| setupSockect(); | |||
| } | |||
| scrollToBottom(false); | |||
| }, [status]); | |||
| return () => { | |||
| closeSocket(); | |||
| }; | |||
| }, [status, start_time, pod_name, isMouseDownRef, setLogList]); | |||
| // 鼠标拖到中不滚动到底部 | |||
| useEffect(() => { | |||
| @@ -66,7 +148,6 @@ function LogGroup({ | |||
| return () => { | |||
| document.removeEventListener('mousedown', mouseDown); | |||
| document.removeEventListener('mouseup', mouseUp); | |||
| closeSocket(); | |||
| }; | |||
| }, [setIsMouseDown]); | |||
| @@ -120,78 +201,7 @@ function LogGroup({ | |||
| requestExperimentPodsLog(); | |||
| }; | |||
| // 建立 socket 连接 | |||
| const setupSockect = () => { | |||
| let { host } = location; | |||
| if (process.env.NODE_ENV === 'development') { | |||
| host = '172.20.32.197:31213'; | |||
| } | |||
| const socket = new WebSocket( | |||
| `ws://${host}/newlog/realtimeLog?start=${start_time}&query={pod="${pod_name}"}`, | |||
| ); | |||
| socket.addEventListener('open', () => { | |||
| console.log('WebSocket is open now.'); | |||
| }); | |||
| socket.addEventListener('close', (event) => { | |||
| console.log('WebSocket is closed:', event); | |||
| // 有时候会出现连接失败,重试 3 次 | |||
| if (event.code !== 1000 && retryRef.current > 0) { | |||
| retryRef.current -= 1; | |||
| setTimeout(() => { | |||
| setupSockect(); | |||
| }, 2 * 1000); | |||
| } | |||
| }); | |||
| socket.addEventListener('error', (event) => { | |||
| console.error('WebSocket error observed:', event); | |||
| }); | |||
| socket.addEventListener('message', (event) => { | |||
| // console.log('message received.', event); | |||
| if (!event.data) { | |||
| return; | |||
| } | |||
| try { | |||
| const data = JSON.parse(event.data); | |||
| const streams = data.streams; | |||
| if (!streams || !Array.isArray(streams)) { | |||
| return; | |||
| } | |||
| let startTime = start_time; | |||
| const logContent = streams.reduce((result, item) => { | |||
| const values = item.values; | |||
| return ( | |||
| result + | |||
| values.reduce((prev: string, cur: [string, string]) => { | |||
| const [time, value] = cur; | |||
| startTime = time; | |||
| const str = `[${dayjs(Number(time) / 1.0e6).format('YYYY-MM-DD HH:mm:ss')}] ${value}`; | |||
| return prev + str; | |||
| }, '') | |||
| ); | |||
| }, ''); | |||
| const logDetail: Log = { | |||
| start_time: startTime!, | |||
| log_content: logContent, | |||
| pod_name: pod_name, | |||
| }; | |||
| setLogList((oldList) => oldList.concat(logDetail)); | |||
| if (!isMouseDownRef.current && logContent) { | |||
| setTimeout(() => { | |||
| scrollToBottom(); | |||
| }, 100); | |||
| } | |||
| } catch (error) { | |||
| console.error('JSON parse error: ', error); | |||
| } | |||
| }); | |||
| socketRef.current = socket; | |||
| }; | |||
| // 关闭 socket | |||
| const closeSocket = () => { | |||
| if (socketRef.current) { | |||
| socketRef.current.close(1000, 'completed'); | |||
| @@ -22,6 +22,8 @@ enum TabKeys { | |||
| History = 'history', | |||
| } | |||
| const NodePrefix = 'workflow'; | |||
| function HyperParameterInstance() { | |||
| const [experimentInfo, setExperimentInfo] = useState<HyperParameterData | undefined>(undefined); | |||
| const [instanceInfo, setInstanceInfo] = useState<HyperParameterInstanceData | undefined>( | |||
| @@ -41,6 +43,7 @@ function HyperParameterInstance() { | |||
| return () => { | |||
| closeSSE(); | |||
| }; | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []); | |||
| // 获取实验实例详情 | |||
| @@ -83,7 +86,7 @@ function HyperParameterInstance() { | |||
| if (nodeStatusJson) { | |||
| setNodes(nodeStatusJson); | |||
| Object.keys(nodeStatusJson).some((key) => { | |||
| if (key.startsWith('workflow')) { | |||
| if (key.startsWith(NodePrefix)) { | |||
| const workflowStatus = nodeStatusJson[key]; | |||
| setWorkflowStatus(workflowStatus); | |||
| return true; | |||
| @@ -101,9 +104,6 @@ function HyperParameterInstance() { | |||
| const setupSSE = (name: string, namespace: string) => { | |||
| const { origin } = location; | |||
| // if (process.env.NODE_ENV === 'development') { | |||
| // origin = 'http://172.20.32.197:31213'; | |||
| // } | |||
| const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`); | |||
| const evtSource = new EventSource( | |||
| `${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`, | |||
| @@ -119,7 +119,7 @@ function HyperParameterInstance() { | |||
| const nodes = dataJson?.result?.object?.status?.nodes; | |||
| if (nodes) { | |||
| const workflowStatus = Object.values(nodes).find((node: any) => | |||
| node.displayName.startsWith('workflow'), | |||
| node.displayName.startsWith(NodePrefix), | |||
| ) as NodeStatus; | |||
| // 节点 | |||
| @@ -56,6 +56,10 @@ function ModelEvolution({ | |||
| useEffect(() => { | |||
| initGraph(); | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []); | |||
| useEffect(() => { | |||
| const changeSize = () => { | |||
| if (!graph || graph.get('destroyed')) return; | |||
| if (!graphRef.current) return; | |||
| @@ -124,7 +124,7 @@ const JobLogTableList: React.FC = () => { | |||
| getDictValueEnum('sys_job_group').then((data) => { | |||
| setJobGroupOptions(data); | |||
| }); | |||
| }, []); | |||
| }, [jobId]); | |||
| const columns: ProColumns<API.Monitor.JobLog>[] = [ | |||
| { | |||
| @@ -32,7 +32,10 @@ const EditPipeline = () => { | |||
| useEffect(() => { | |||
| initGraph(); | |||
| getFirstWorkflow(locationParams.id); | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, []); | |||
| useEffect(() => { | |||
| const changeSize = () => { | |||
| if (!graph || graph.get('destroyed')) return; | |||
| if (!graphRef.current) return; | |||
| @@ -1,7 +1,7 @@ | |||
| import { getComponentAll } from '@/services/pipeline/index.js'; | |||
| import { PipelineNodeModel } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Collapse, type CollapseProps } from 'antd'; | |||
| import { Collapse } from 'antd'; | |||
| import { useEffect, useState } from 'react'; | |||
| import Styles from './index.less'; | |||
| @@ -18,7 +18,6 @@ type ModelMenuProps = { | |||
| }; | |||
| const ModelMenu = ({ onComponentDragEnd }: ModelMenuProps) => { | |||
| const [modelMenusList, setModelMenusList] = useState<ModelMenuData[]>([]); | |||
| const [collapseItems, setCollapseItems] = useState<CollapseProps['items']>([]); | |||
| useEffect(() => { | |||
| // 获取所有组件 | |||
| @@ -27,35 +26,6 @@ const ModelMenu = ({ onComponentDragEnd }: ModelMenuProps) => { | |||
| if (res && res.data) { | |||
| const menus = res.data as ModelMenuData[]; | |||
| setModelMenusList(menus); | |||
| const items = menus.map((item) => { | |||
| return { | |||
| key: item.key, | |||
| label: item.name, | |||
| children: item.value.map((ele) => { | |||
| return ( | |||
| <div | |||
| key={ele.id} | |||
| draggable="true" | |||
| onDragEnd={(e) => { | |||
| dragEnd(e, ele); | |||
| }} | |||
| className={Styles.collapseItem} | |||
| > | |||
| {ele.icon_path && ( | |||
| <img | |||
| style={{ height: '16px', marginRight: '15px' }} | |||
| src={`/assets/images/${ele.icon_path}.png`} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| )} | |||
| {ele.component_label} | |||
| </div> | |||
| ); | |||
| }), | |||
| }; | |||
| }); | |||
| setCollapseItems(items); | |||
| } | |||
| }; | |||
| @@ -73,6 +43,35 @@ const ModelMenu = ({ onComponentDragEnd }: ModelMenuProps) => { | |||
| }; | |||
| const defaultActiveKey = modelMenusList.map((item) => item.key + ''); | |||
| const items = modelMenusList.map((item) => { | |||
| return { | |||
| key: item.key, | |||
| label: item.name, | |||
| children: item.value.map((ele) => { | |||
| return ( | |||
| <div | |||
| key={ele.id} | |||
| draggable="true" | |||
| onDragEnd={(e) => { | |||
| dragEnd(e, ele); | |||
| }} | |||
| className={Styles.collapseItem} | |||
| > | |||
| {ele.icon_path && ( | |||
| <img | |||
| style={{ height: '16px', marginRight: '15px' }} | |||
| src={`/assets/images/${ele.icon_path}.png`} | |||
| draggable={false} | |||
| alt="" | |||
| /> | |||
| )} | |||
| {ele.component_label} | |||
| </div> | |||
| ); | |||
| }), | |||
| }; | |||
| }); | |||
| return ( | |||
| <div className={Styles.collapse}> | |||
| <div className={Styles.modelMenusTitle}>组件库</div> | |||
| @@ -82,7 +81,7 @@ const ModelMenu = ({ onComponentDragEnd }: ModelMenuProps) => { | |||
| collapsible="header" | |||
| expandIconPosition="end" | |||
| defaultActiveKey={defaultActiveKey} | |||
| items={collapseItems} | |||
| items={items} | |||
| ></Collapse> | |||
| ) : null} | |||
| </div> | |||
| @@ -19,31 +19,37 @@ const DeptTree: React.FC<TreeProps> = (props) => { | |||
| const [treeData, setTreeData] = useState<any>([]); | |||
| const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]); | |||
| const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]); | |||
| const fetchDeptList = async () => { | |||
| const hide = message.loading('正在查询'); | |||
| try { | |||
| const res = await getDeptTree({}); | |||
| const treeData = res.map((item: any) => ({ ...item, key: item.id })); | |||
| setTreeData(treeData); | |||
| props.onSelect(treeData[0]); | |||
| setExpandedKeys([treeData[0].key]); | |||
| setSelectedKeys([treeData[0].key]); | |||
| hide(); | |||
| return true; | |||
| } catch (error) { | |||
| hide(); | |||
| return false; | |||
| } | |||
| }; | |||
| const { onSelect } = props; | |||
| useEffect(() => { | |||
| const fetchDeptList = async () => { | |||
| const hide = message.loading('正在查询'); | |||
| try { | |||
| const res = await getDeptTree({}); | |||
| const treeData = res.map((item: any) => ({ ...item, key: item.id })); | |||
| setTreeData(treeData); | |||
| setExpandedKeys([treeData[0].key]); | |||
| setSelectedKeys([treeData[0].key]); | |||
| hide(); | |||
| return true; | |||
| } catch (error) { | |||
| hide(); | |||
| return false; | |||
| } | |||
| }; | |||
| fetchDeptList(); | |||
| }, []); | |||
| const onSelect = (keys: React.Key[], info: any) => { | |||
| useEffect(() => { | |||
| if (treeData.length > 0) { | |||
| onSelect(treeData[0]); | |||
| } | |||
| }, [treeData, onSelect]); | |||
| const handleSelect = (keys: React.Key[], info: any) => { | |||
| setSelectedKeys(keys); | |||
| props.onSelect(info.node); | |||
| onSelect(info.node); | |||
| }; | |||
| const onExpand = (keys: React.Key[]) => { | |||
| @@ -57,7 +63,7 @@ const DeptTree: React.FC<TreeProps> = (props) => { | |||
| onExpand={onExpand} | |||
| expandedKeys={expandedKeys} | |||
| selectedKeys={selectedKeys} | |||
| onSelect={onSelect} | |||
| onSelect={handleSelect} | |||
| treeData={treeData} | |||
| /> | |||
| ); | |||
| @@ -114,6 +114,7 @@ const TableList: React.FC = () => { | |||
| }; | |||
| useEffect(() => { | |||
| setStepComponent(getCurrentStepAndComponent(stepKey)); | |||
| // eslint-disable-next-line react-hooks/exhaustive-deps | |||
| }, [stepKey]); | |||
| useEffect(() => { | |||
| @@ -150,7 +151,7 @@ const TableList: React.FC = () => { | |||
| message.error(res.msg); | |||
| } | |||
| }); | |||
| }, []); | |||
| }, [tableId]); | |||
| // const onFinish = (values: any) => { | |||
| // console.log('Success:', values); | |||