Skip to content

Commit 3cbff60

Browse files
authored
[GSOC][RIP-78][ISSUES#308] Add part of refactored front-end files (#312)
1 parent bd94e8c commit 3cbff60

File tree

9 files changed

+3797
-0
lines changed

9 files changed

+3797
-0
lines changed

frontend-new/src/pages/Acl/acl.jsx

Lines changed: 676 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import React, {useCallback, useEffect, useState} from 'react';
19+
import {Button, Modal, notification, Select, Spin, Table} from 'antd';
20+
import {useLanguage} from "../../i18n/LanguageContext";
21+
import {remoteApi, tools} from "../../api/remoteApi/remoteApi"; // 确保路径正确
22+
23+
const {Option} = Select;
24+
25+
const Cluster = () => {
26+
const {t} = useLanguage();
27+
28+
const [loading, setLoading] = useState(false);
29+
const [clusterNames, setClusterNames] = useState([]);
30+
const [selectedCluster, setSelectedCluster] = useState('');
31+
const [instances, setInstances] = useState([]);
32+
const [allBrokersData, setAllBrokersData] = useState({});
33+
34+
const [detailModalVisible, setDetailModalVisible] = useState(false);
35+
const [configModalVisible, setConfigModalVisible] = useState(false);
36+
const [currentDetail, setCurrentDetail] = useState({});
37+
const [currentConfig, setCurrentConfig] = useState({});
38+
const [currentBrokerName, setCurrentBrokerName] = useState('');
39+
const [currentIndex, setCurrentIndex] = useState(null); // 对应 brokerId
40+
const [currentBrokerAddress, setCurrentBrokerAddress] = useState('');
41+
const [api, contextHolder] = notification.useNotification();
42+
43+
const switchCluster = useCallback((clusterName) => {
44+
if (allBrokersData[clusterName]) {
45+
setInstances(allBrokersData[clusterName]);
46+
} else {
47+
setInstances([]);
48+
}
49+
}, [allBrokersData]);
50+
51+
const handleChangeCluster = (value) => {
52+
setSelectedCluster(value);
53+
switchCluster(value);
54+
};
55+
56+
useEffect(() => {
57+
setLoading(true);
58+
remoteApi.queryClusterList((resp) => {
59+
setLoading(false);
60+
if (resp.status === 0) {
61+
const {clusterInfo, brokerServer} = resp.data;
62+
const {clusterAddrTable, brokerAddrTable} = clusterInfo;
63+
64+
const generatedBrokers = tools.generateBrokerMap(brokerServer, clusterAddrTable, brokerAddrTable);
65+
setAllBrokersData(generatedBrokers);
66+
67+
const names = Object.keys(clusterAddrTable);
68+
setClusterNames(names);
69+
70+
if (names.length > 0) {
71+
const defaultCluster = names[0];
72+
setSelectedCluster(defaultCluster);
73+
if (generatedBrokers[defaultCluster]) {
74+
setInstances(generatedBrokers[defaultCluster]);
75+
} else {
76+
setInstances([]);
77+
}
78+
}
79+
80+
} else {
81+
api.error({message: resp.errMsg || t.QUERY_CLUSTER_LIST_FAILED, duration: 2});
82+
}
83+
});
84+
}, []);
85+
86+
const showDetail = (brokerName, brokerId, record) => { // 传入 record 整个对象,方便直接显示
87+
setCurrentBrokerName(brokerName);
88+
setCurrentIndex(brokerId);
89+
setCurrentDetail(record); // 直接使用 record 作为详情
90+
setDetailModalVisible(true);
91+
};
92+
93+
const showConfig = (brokerAddress, brokerName, brokerId) => { // 保持一致,传入 brokerId
94+
setCurrentBrokerName(brokerName);
95+
setCurrentIndex(brokerId);
96+
setCurrentBrokerAddress(brokerAddress);
97+
98+
setLoading(true);
99+
remoteApi.queryBrokerConfig(brokerAddress, (resp) => {
100+
setLoading(false);
101+
if (resp.status === 0) {
102+
// ✨ 确保 resp.data 是一个对象,如果后端返回的不是对象,这里需要处理
103+
if (typeof resp.data === 'object' && resp.data !== null) {
104+
setCurrentConfig(resp.data);
105+
setConfigModalVisible(true);
106+
} else {
107+
api.error({message: t.INVALID_CONFIG_DATA || 'Invalid config data received', duration: 2});
108+
setCurrentConfig({}); // 清空配置,避免显示错误
109+
}
110+
} else {
111+
api.error({message: resp.errMsg || t.QUERY_BROKER_CONFIG_FAILED, duration: 2});
112+
}
113+
});
114+
};
115+
116+
const columns = [
117+
{
118+
title: t.SPLIT,
119+
dataIndex: 'brokerName', // 直接使用 brokerId
120+
key: 'split',
121+
align: 'center'
122+
},
123+
{
124+
title: t.NO,
125+
dataIndex: 'brokerId', // 直接使用 brokerId
126+
key: 'no',
127+
align: 'center',
128+
render: (brokerId) => `${brokerId}${brokerId === 0 ? `(${t.MASTER})` : `(${t.SLAVE})`}`,
129+
},
130+
{
131+
title: t.ADDRESS,
132+
dataIndex: 'address', // 确保 generateBrokerMap 返回的数据有 address 字段
133+
key: 'address',
134+
align: 'center',
135+
},
136+
{
137+
title: t.VERSION,
138+
dataIndex: 'brokerVersionDesc',
139+
key: 'version',
140+
align: 'center',
141+
},
142+
{
143+
title: t.PRO_MSG_TPS,
144+
dataIndex: 'putTps',
145+
key: 'putTps',
146+
align: 'center',
147+
render: (text) => {
148+
const tpsValue = text ? Number(String(text).split(' ')[0]) : 0; // 确保text是字符串
149+
return tpsValue.toFixed(2);
150+
},
151+
},
152+
{
153+
title: t.CUS_MSG_TPS,
154+
key: 'cusMsgTps',
155+
align: 'center',
156+
render: (_, record) => {
157+
// 根据你提供的数据结构,这里可能是 getTransferredTps
158+
const val = record.getTransferedTps?.trim() ? record.getTransferedTps : record.getTransferredTps;
159+
const tpsValue = val ? Number(String(val).split(' ')[0]) : 0; // 确保val是字符串
160+
return tpsValue.toFixed(2);
161+
},
162+
},
163+
{
164+
title: t.YESTERDAY_PRO_COUNT,
165+
key: 'yesterdayProCount',
166+
align: 'center',
167+
render: (_, record) => {
168+
const putTotalTodayMorning = parseFloat(record.msgPutTotalTodayMorning || 0);
169+
const putTotalYesterdayMorning = parseFloat(record.msgPutTotalYesterdayMorning || 0);
170+
return (putTotalTodayMorning - putTotalYesterdayMorning).toLocaleString();
171+
}
172+
},
173+
{
174+
title: t.YESTERDAY_CUS_COUNT,
175+
key: 'yesterdayCusCount',
176+
align: 'center',
177+
render: (_, record) => {
178+
const getTotalTodayMorning = parseFloat(record.msgGetTotalTodayMorning || 0);
179+
const getTotalYesterdayMorning = parseFloat(record.msgGetTotalYesterdayMorning || 0);
180+
return (getTotalTodayMorning - getTotalYesterdayMorning).toLocaleString();
181+
}
182+
},
183+
{
184+
title: t.TODAY_PRO_COUNT,
185+
key: 'todayProCount',
186+
align: 'center',
187+
render: (_, record) => {
188+
const putTotalTodayNow = parseFloat(record.msgPutTotalTodayNow || 0);
189+
const putTotalTodayMorning = parseFloat(record.msgPutTotalTodayMorning || 0);
190+
return (putTotalTodayNow - putTotalTodayMorning).toLocaleString();
191+
}
192+
},
193+
{
194+
title: t.TODAY_CUS_COUNT,
195+
key: 'todayCusCount',
196+
align: 'center',
197+
render: (_, record) => {
198+
const getTotalTodayNow = parseFloat(record.msgGetTotalTodayNow || 0);
199+
const getTotalTodayMorning = parseFloat(record.msgGetTotalTodayMorning || 0);
200+
return (getTotalTodayNow - getTotalTodayMorning).toLocaleString();
201+
}
202+
},
203+
{
204+
title: t.OPERATION,
205+
key: 'operation',
206+
align: 'center',
207+
render: (_, record) => (
208+
<>
209+
<Button size="small" type="primary"
210+
onClick={() => showDetail(record.brokerName, record.brokerId, record)}
211+
style={{marginRight: 8}}>
212+
{t.STATUS}
213+
</Button>
214+
{/* 传入 record.address */}
215+
<Button size="small" type="primary"
216+
onClick={() => showConfig(record.address, record.brokerName, record.brokerId)}>
217+
{t.CONFIG}
218+
</Button>
219+
</>
220+
),
221+
},
222+
];
223+
224+
return (
225+
<>
226+
{contextHolder}
227+
<Spin spinning={loading} tip={t.LOADING}>
228+
<div style={{padding: 24}}>
229+
<div style={{marginBottom: 16, display: 'flex', alignItems: 'center'}}>
230+
<label style={{marginRight: 8}}>{t.CLUSTER}:</label>
231+
<Select
232+
style={{width: 300}}
233+
placeholder={t.SELECT_CLUSTER || "Please select a cluster"}
234+
value={selectedCluster}
235+
onChange={handleChangeCluster}
236+
allowClear
237+
>
238+
{clusterNames.map((name) => (
239+
<Option key={name} value={name}>
240+
{name}
241+
</Option>
242+
))}
243+
</Select>
244+
</div>
245+
246+
<Table
247+
dataSource={instances}
248+
columns={columns}
249+
rowKey={(record) => `${record.brokerName}-${record.brokerId}`}
250+
pagination={false}
251+
bordered
252+
size="middle"
253+
/>
254+
255+
<Modal
256+
title={`${t.BROKER} [${currentBrokerName}][${currentIndex}]`}
257+
open={detailModalVisible}
258+
footer={null}
259+
onCancel={() => setDetailModalVisible(false)}
260+
width={800}
261+
bodyStyle={{maxHeight: '60vh', overflowY: 'auto'}}
262+
>
263+
<Table
264+
dataSource={Object.entries(currentDetail).map(([key, value]) => ({key, value}))}
265+
columns={[
266+
{title: t.KEY || 'Key', dataIndex: 'key', key: 'key'},
267+
{title: t.VALUE || 'Value', dataIndex: 'value', key: 'value'},
268+
]}
269+
pagination={false}
270+
size="small"
271+
bordered
272+
rowKey="key"
273+
/>
274+
</Modal>
275+
276+
<Modal
277+
title={`${t.BROKER} [${currentBrokerName}][${currentIndex}]`}
278+
open={configModalVisible}
279+
footer={null}
280+
onCancel={() => setConfigModalVisible(false)}
281+
width={800}
282+
bodyStyle={{maxHeight: '60vh', overflowY: 'auto'}}
283+
>
284+
<Table
285+
dataSource={Object.entries(currentConfig).map(([key, value]) => ({key, value}))}
286+
columns={[
287+
{title: t.KEY || 'Key', dataIndex: 'key', key: 'key'},
288+
{title: t.VALUE || 'Value', dataIndex: 'value', key: 'value'},
289+
]}
290+
pagination={false}
291+
size="small"
292+
bordered
293+
rowKey="key"
294+
/>
295+
</Modal>
296+
</div>
297+
</Spin>
298+
</>
299+
300+
);
301+
};
302+
303+
export default Cluster;

0 commit comments

Comments
 (0)