feat: worker terminal

This commit is contained in:
jialin
2025-10-13 14:40:26 +08:00
parent a9de153c00
commit 1a693f70ac
6 changed files with 163 additions and 3 deletions
+1 -1
View File
@@ -1,11 +1,11 @@
import useUserSettings from '@/hooks/use-user-settings';
import { FitAddon } from '@xterm/addon-fit';
import { Terminal } from '@xterm/xterm';
import '@xterm/xterm/css/xterm.css';
import { createStyles } from 'antd-style';
import _ from 'lodash';
import qs from 'query-string';
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
import 'xterm/css/xterm.css';
const useStyles = createStyles(({ token, css }) => ({
wrap: css`
@@ -0,0 +1,40 @@
.ant-pro-footer-bar {
padding-inline: 0;
border-block-start: 1px solid rgba(5, 5, 5, 6%);
background-color: #f5f5f5;
border-radius: 4px 4px 0 0;
.ant-pro-footer-bar-left {
display: none;
}
.ant-pro-footer-bar-right {
flex: 1;
}
.ant-tabs-nav {
.ant-tabs-tab {
border: none;
background-color: transparent;
.ant-tabs-tab-remove {
display: none;
}
&:hover {
.ant-tabs-tab-remove {
display: inline-block;
}
}
}
.ant-tabs-tab:not(.ant-tabs-tab-active) {
color: var(--ant-color-text-secondary);
}
.ant-tabs-tab-active {
background-color: var(--ant-color-bg-elevated);
color: var(--ant-color-text);
}
}
}
@@ -0,0 +1,30 @@
import './footer.less';
import TerminalTabs from './tabs';
export interface TerminalModalProps {
terminals: { url: string; name: string }[];
height: number;
open: boolean;
onClose: () => void;
currentActive?: string;
}
const TerminalModal: React.FC<TerminalModalProps> = ({
terminals,
height,
open,
onClose,
currentActive
}) => {
return (
<div>
<TerminalTabs
terminals={terminals}
height={height}
currentActive={currentActive}
/>
</div>
);
};
export default TerminalModal;
@@ -0,0 +1,49 @@
import XTerminal from '@/components/x-terminal';
import { Tabs } from 'antd';
import React, { useEffect, useMemo, useState } from 'react';
import { TerminalProps } from './types';
export interface TerminalTabsProps {
terminals: TerminalProps[];
height: number;
currentActive?: string;
}
const TerminalTabs: React.FC<TerminalTabsProps> = ({
terminals,
height,
currentActive
}) => {
const [activeKey, setActiveKey] = useState(
currentActive || terminals[0]?.url || ''
);
const items = useMemo(() => {
return terminals.map((terminal) => {
return {
key: terminal.url,
label: terminal.name,
children: <XTerminal height={height} url={terminal.url} />
};
});
}, [terminals]);
useEffect(() => {
if (currentActive) {
setActiveKey(currentActive);
}
}, [currentActive]);
return (
<Tabs
type="editable-card"
hideAdd
activeKey={activeKey}
onChange={setActiveKey}
tabBarStyle={{ marginBottom: 0 }}
items={items}
></Tabs>
);
};
export default TerminalTabs;
@@ -0,0 +1,4 @@
export interface TerminalProps {
url: string;
name: string;
}
+39 -2
View File
@@ -7,6 +7,7 @@ import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Empty, Table, message } from 'antd';
import React, { useEffect, useState } from 'react';
import TerminalTabs from '../../_components/terminal-tabs';
import {
WORKERS_API,
deleteWorker,
@@ -64,6 +65,16 @@ const Workers: React.FC = () => {
open: false,
currentData: null
});
const [terminals, setTerminals] = useState<{ url: string; name: string }[]>(
[]
);
const [terminalModalStatus, setTerminalModalStatus] = useState<{
open: boolean;
currentActive: string;
}>({
open: false,
currentActive: ''
});
const getClusterList = async () => {
try {
@@ -97,7 +108,6 @@ const Workers: React.FC = () => {
const handleUpdateLabelsOk = async (values: Record<string, any>) => {
try {
console.log('updateLabelsData.data', updateLabelsData.data);
await updateWorker(updateLabelsData.data.id, {
...updateLabelsData.data,
labels: values.labels
@@ -118,7 +128,6 @@ const Workers: React.FC = () => {
};
const handleUpdateLabels = (record: ListItem) => {
console.log('record', record);
setUpdateLabelsData({
open: true,
data: {
@@ -151,6 +160,19 @@ const Workers: React.FC = () => {
name: record.name
});
}
if (val === 'terminal') {
const url = `/api/workers/${record.id}/terminal`;
setTerminals((prev) => {
return [
...prev,
{
url,
name: record.name
}
];
});
setTerminalModalStatus({ open: true, currentActive: url });
}
});
const renderEmpty = (type?: string) => {
@@ -172,6 +194,11 @@ const Workers: React.FC = () => {
});
};
const handleTerminalClose = () => {
setTerminalModalStatus({ open: false, currentActive: '' });
setTerminals([]);
};
const columns = useWorkerColumns({
clusterData,
loadend: dataSource.loadend,
@@ -187,6 +214,16 @@ const Workers: React.FC = () => {
<>
<PageContainer
ghost
footer={[
<TerminalTabs
height={300}
key="terminal-tabs"
terminals={terminals}
open={terminalModalStatus.open}
currentActive={terminalModalStatus.currentActive}
onClose={handleTerminalClose}
/>
]}
header={{
title: 'Workers',
style: {