diff --git a/ui/src/components/Terminal/terminal.tsx b/ui/src/components/Terminal/terminal.tsx index 70965edd4..c2ab5e3f3 100644 --- a/ui/src/components/Terminal/terminal.tsx +++ b/ui/src/components/Terminal/terminal.tsx @@ -1,96 +1,116 @@ -import { Terminal } from '@xterm/xterm' -import { Button, Modal } from 'antd' -import React from 'react' +import { intl } from '@/utils/intl'; +import { Terminal } from '@xterm/xterm'; +import { Button, Modal } from 'antd'; +import React from 'react'; export interface ITerminal { - terminalId: string - onClose?: () => void - onConnected?: () => void + terminalId: string; + onClose?: () => void; + onConnected?: () => void; } function devLog(...args: any[]) { if (process.env.NODE_ENV === 'development') { - console.log(...args) + console.log(...args); } } export const OBTerminal: React.FC = (props) => { - const { terminalId } = props - const ref = React.useRef(null) - const [ws, setWs] = React.useState(null) + const { terminalId } = props; + const ref = React.useRef(null); + const [ws, setWs] = React.useState(null); React.useEffect(() => { if (ref.current) { const term = new Terminal({ cols: 160, rows: 60, - }) - term.open(ref.current) + }); + term.open(ref.current); if (term.options.fontSize) { - const containerWidth = ref.current.clientWidth + const containerWidth = ref.current.clientWidth; - const cols = Math.floor(containerWidth / 9.2) - const rows = Math.floor(cols / 4) - term.resize(cols, rows) - const ws = new WebSocket(`ws://${location.host}/api/v1/terminal/${terminalId}?cols=${cols}&rows=${rows}`) - term.write('Hello from \x1B[1;3;31mOceanBase\x1B[0m\r\n') + const cols = Math.floor(containerWidth / 9.2); + const rows = Math.floor(cols / 4); + term.resize(cols, rows); + const ws = new WebSocket( + `ws://${location.host}/api/v1/terminal/${terminalId}?cols=${cols}&rows=${rows}`, + ); + term.write('Hello from \x1B[1;3;31mOceanBase\x1B[0m\r\n'); ws.onopen = function () { - devLog('Websocket connection open ...') + devLog('Websocket connection open ...'); // ws.send(JSON.stringify({ type: 'ping' })) term.onData(function (data) { - ws.send(data) - }) - props.onConnected?.() - setWs(ws) + ws.send(data); + }); + props.onConnected?.(); + setWs(ws); window.addEventListener('beforeunload', () => { if (ws) { - ws.close() - props.onClose?.() - setWs(null) + ws.close(); + props.onClose?.(); + setWs(null); } - }) - } + }); + }; ws.onmessage = function (event) { - term.write(event.data) - } + term.write(event.data); + }; ws.onclose = function () { - devLog('Connection closed.') - term.write('\r\nConnection closed.\r\n') - } + devLog('Connection closed.'); + term.write('\r\nConnection closed.\r\n'); + }; ws.onerror = function (evt) { - console.error('WebSocket error observed:', evt) - } + console.error('WebSocket error observed:', evt); + }; } } return () => { - window.removeEventListener('beforeunload', () => { }) - } - }, []) + window.removeEventListener('beforeunload', () => {}); + }; + }, []); return ( <> - {ws &&
- -
} + {ws && ( +
+ +
+ )}
- ) -} + ); +}; diff --git a/ui/src/i18n/strings/en-US.json b/ui/src/i18n/strings/en-US.json index a8ec8e7cc..d840869b0 100644 --- a/ui/src/i18n/strings/en-US.json +++ b/ui/src/i18n/strings/en-US.json @@ -695,5 +695,15 @@ "Dashboard.Detail.Overview.BasicInfo.ClusterInformation": "Cluster Information", "Dashboard.Detail.Overview.ServerTable.ServerList": "Server List", "Dashboard.Detail.Overview.ZoneTable.ZoneList": "Zone List", - "Dashboard.Detail.NewBackup.AdvancedConfiguration.Days": "Days" + "Dashboard.Detail.NewBackup.AdvancedConfiguration.Days": "Days", + "Dashboard.Cluster.Detail.Connection1": "Connection", + "Dashboard.Cluster.Detail.Connection": "Cluster connection", + "Dashboard.Tenant.Detail.Connection1": "Connection", + "Dashboard.Cluster.Detail.CloseConnection": "Connection closed", + "Dashboard.Cluster.Detail.NotRunning": "Cluster is not running", + "Dashboard.Cluster.Detail.CreateConnection": "Create connection", + "Dashboard.components.Terminal.Disconnect": "Disconnect", + "Dashboard.components.Terminal.Disconnect1": "Are you sure you want to disconnect?", + "Dashboard.Tenant.Detail.Connection": "Tenant connection", + "Dashboard.Cluster.Detail.AbnormalOperation": "Tenant is not functioning properly" } diff --git a/ui/src/i18n/strings/zh-CN.json b/ui/src/i18n/strings/zh-CN.json index 436474a21..7adb32805 100644 --- a/ui/src/i18n/strings/zh-CN.json +++ b/ui/src/i18n/strings/zh-CN.json @@ -695,5 +695,15 @@ "Dashboard.Detail.Overview.BasicInfo.ClusterInformation": "集群信息", "Dashboard.Detail.Overview.ServerTable.ServerList": "Server 列表", "Dashboard.Detail.Overview.ZoneTable.ZoneList": "Zone 列表", - "Dashboard.Detail.NewBackup.AdvancedConfiguration.Days": "天" + "Dashboard.Detail.NewBackup.AdvancedConfiguration.Days": "天", + "Dashboard.Cluster.Detail.Connection": "集群连接", + "Dashboard.Cluster.Detail.Connection1": "集群连接", + "Dashboard.Tenant.Detail.Connection1": "连接租户", + "Dashboard.Cluster.Detail.CloseConnection": "连接已关闭", + "Dashboard.Cluster.Detail.NotRunning": "集群未运行", + "Dashboard.Cluster.Detail.CreateConnection": "创建连接", + "Dashboard.components.Terminal.Disconnect": "断开连接", + "Dashboard.components.Terminal.Disconnect1": "确定要断开连接吗?", + "Dashboard.Tenant.Detail.Connection": "连接租户", + "Dashboard.Cluster.Detail.AbnormalOperation": "租户未正常运行" } diff --git a/ui/src/pages/Cluster/Detail/Connection/index.tsx b/ui/src/pages/Cluster/Detail/Connection/index.tsx index e95b95c28..0eab810c5 100644 --- a/ui/src/pages/Cluster/Detail/Connection/index.tsx +++ b/ui/src/pages/Cluster/Detail/Connection/index.tsx @@ -1,40 +1,45 @@ -import React, { useState } from 'react' -import { PageContainer } from '@ant-design/pro-components' -import { intl } from '@/utils/intl' -import { OBTerminal } from '@/components/Terminal/terminal' -import { Button, Row, message } from 'antd' -import { request, useParams } from '@umijs/max' -import { useRequest } from 'ahooks' -import { getClusterDetailReq } from '@/services' -import BasicInfo from '../Overview/BasicInfo' - +import { OBTerminal } from '@/components/Terminal/terminal'; +import { getClusterDetailReq } from '@/services'; +import { intl } from '@/utils/intl'; +import { PageContainer } from '@ant-design/pro-components'; +import { request, useParams } from '@umijs/max'; +import { useRequest } from 'ahooks'; +import { Button, Row, message } from 'antd'; +import React, { useState } from 'react'; +import BasicInfo from '../Overview/BasicInfo'; const ClusterConnection: React.FC = () => { const header = () => { return { title: intl.formatMessage({ - id: 'dashboard.Cluster.Detail.Connection', + id: 'Dashboard.Cluster.Detail.Connection', defaultMessage: '集群连接', - }) - } - } - const {ns, name} = useParams(); + }), + }; + }; + const { ns, name } = useParams(); const { data: clusterDetail } = useRequest(getClusterDetailReq, { defaultParams: [{ name: name!, ns: ns! }], - }) + }); - const { runAsync } = useRequest(async (): Promise<{ - data: { terminalId: string } - }> => { - return request(`/api/v1/obclusters/namespace/${ns}/name/${name}/terminal`, { - method: 'PUT' - }) - }, { - manual: true - }) + const { runAsync } = useRequest( + async (): Promise<{ + data: { terminalId: string }; + }> => { + return request( + `/api/v1/obclusters/namespace/${ns}/name/${name}/terminal`, + { + method: 'PUT', + }, + ); + }, + { + manual: true, + }, + ); - const [terminalId, setTerminalId] = useState() + const [terminalId, setTerminalId] = useState(); return ( @@ -46,28 +51,50 @@ const ClusterConnection: React.FC = () => { {clusterDetail && ( )} -
+
{terminalId ? ( - { - setTerminalId(undefined) - message.info('连接已关闭') - }} /> + { + setTerminalId(undefined); + message.info( + intl.formatMessage({ + id: 'Dashboard.Cluster.Detail.CloseConnection', + defaultMessage: '连接已关闭', + }), + ); + }} + /> ) : ( - + )}
- ) -} + ); +}; -export default ClusterConnection +export default ClusterConnection; diff --git a/ui/src/pages/Cluster/Detail/index.tsx b/ui/src/pages/Cluster/Detail/index.tsx index 9f406b9d2..51260efba 100644 --- a/ui/src/pages/Cluster/Detail/index.tsx +++ b/ui/src/pages/Cluster/Detail/index.tsx @@ -87,7 +87,7 @@ const ClusterDetail: React.FC = () => { }, { title: intl.formatMessage({ - id: 'Dashboard.Cluster.Detail.Connection', + id: 'Dashboard.Cluster.Detail.Connection1', defaultMessage: '连接集群', }), key: 'connection', diff --git a/ui/src/pages/Tenant/Detail/Connection/index.tsx b/ui/src/pages/Tenant/Detail/Connection/index.tsx index 7d9c9016b..8523e0837 100644 --- a/ui/src/pages/Tenant/Detail/Connection/index.tsx +++ b/ui/src/pages/Tenant/Detail/Connection/index.tsx @@ -1,45 +1,51 @@ -import React, { useEffect, useState } from 'react' -import { PageContainer } from '@ant-design/pro-components' -import { intl } from '@/utils/intl' -import { OBTerminal } from '@/components/Terminal/terminal' -import { Button, Row, message } from 'antd' -import { request, useParams } from '@umijs/max' -import { useRequest } from 'ahooks' -import BasicInfo from '../Overview/BasicInfo' -import { getTenant } from '@/services/tenant' - +import { OBTerminal } from '@/components/Terminal/terminal'; +import { getTenant } from '@/services/tenant'; +import { intl } from '@/utils/intl'; +import { PageContainer } from '@ant-design/pro-components'; +import { request, useParams } from '@umijs/max'; +import { useRequest } from 'ahooks'; +import { Button, Row, message } from 'antd'; +import React, { useEffect, useState } from 'react'; +import BasicInfo from '../Overview/BasicInfo'; const TenantConnection: React.FC = () => { const header = () => { return { title: intl.formatMessage({ - id: 'dashboard.Tenant.Detail.Connection', + id: 'Dashboard.Tenant.Detail.Connection', defaultMessage: '连接租户', - }) - } - } + }), + }; + }; - const {ns, name} = useParams(); + const { ns, name } = useParams(); - const { data: tenantDetailResponse, run: getTenantDetail, loading } = useRequest(getTenant, { + const { + data: tenantDetailResponse, + run: getTenantDetail, + loading, + } = useRequest(getTenant, { manual: true, }); - const { runAsync } = useRequest(async (): Promise<{ - data: { terminalId: string } - }> => { - return request(`/api/v1/obtenants/${ns}/${name}/terminal`, { - method: 'PUT' - }) - }, { - manual: true - }) + const { runAsync } = useRequest( + async (): Promise<{ + data: { terminalId: string }; + }> => { + return request(`/api/v1/obtenants/${ns}/${name}/terminal`, { + method: 'PUT', + }); + }, + { + manual: true, + }, + ); useEffect(() => { getTenantDetail({ ns: ns!, name: name! }); }, []); - const [terminalId, setTerminalId] = useState() + const [terminalId, setTerminalId] = useState(); const tenantDetail = tenantDetailResponse?.data; @@ -51,30 +57,54 @@ const TenantConnection: React.FC = () => { /> {tenantDetail && ( - + )} -
+
{terminalId ? ( - { - setTerminalId(undefined) - message.info('连接已关闭') - }} /> + { + setTerminalId(undefined); + message.info( + intl.formatMessage({ + id: 'Dashboard.Cluster.Detail.CloseConnection', + defaultMessage: '连接已关闭', + }), + ); + }} + /> ) : ( - + )}
- ) -} + ); +}; -export default TenantConnection +export default TenantConnection; diff --git a/ui/src/pages/Tenant/Detail/index.tsx b/ui/src/pages/Tenant/Detail/index.tsx index 801c6c9e9..a3787ffa2 100644 --- a/ui/src/pages/Tenant/Detail/index.tsx +++ b/ui/src/pages/Tenant/Detail/index.tsx @@ -85,7 +85,7 @@ const TenantDetail: React.FC = () => { }, { title: intl.formatMessage({ - id: 'Dashboard.Tenant.Detail.Connection', + id: 'Dashboard.Tenant.Detail.Connection1', defaultMessage: '连接租户', }), key: 'connection',