This is an automated email from the ASF dual-hosted git repository.
jinrongtong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new 07793d8 [ISSUE #329] Add Frontend Proxy Component Support (#336)
07793d8 is described below
commit 07793d8aae1139efef4ab30a0773961b88ff0238
Author: Crazylychee <[email protected]>
AuthorDate: Sat Jul 5 20:53:57 2025 +0800
[ISSUE #329] Add Frontend Proxy Component Support (#336)
---
frontend-new/src/api/remoteApi/remoteApi.js | 4 +-
frontend-new/src/i18n/index.js | 13 +++
frontend-new/src/pages/Consumer/consumer.jsx | 123 ++++++++++++++++++---
frontend-new/src/pages/Dashboard/DashboardPage.jsx | 13 ++-
frontend-new/src/pages/DlqMessage/dlqmessage.jsx | 3 -
frontend-new/src/pages/Message/message.jsx | 4 -
6 files changed, 130 insertions(+), 30 deletions(-)
diff --git a/frontend-new/src/api/remoteApi/remoteApi.js
b/frontend-new/src/api/remoteApi/remoteApi.js
index 94cec36..1df36cf 100644
--- a/frontend-new/src/api/remoteApi/remoteApi.js
+++ b/frontend-new/src/api/remoteApi/remoteApi.js
@@ -376,9 +376,9 @@ const remoteApi = {
}
},
- queryConsumerGroupList: async (skipSysGroup = false) => {
+ queryConsumerGroupList: async (skipSysGroup, address) => {
try {
- const response = await
remoteApi._fetch(remoteApi.buildUrl(`/consumer/groupList.query?skipSysGroup=${skipSysGroup}`));
+ const response = await
remoteApi._fetch(remoteApi.buildUrl(`/consumer/groupList.query?skipSysGroup=${skipSysGroup}&address=${address}`));
const data = await response.json();
return data;
} catch (error) {
diff --git a/frontend-new/src/i18n/index.js b/frontend-new/src/i18n/index.js
index 612af61..1a5fa71 100644
--- a/frontend-new/src/i18n/index.js
+++ b/frontend-new/src/i18n/index.js
@@ -278,6 +278,12 @@ export const translations = {
"ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR",
"PLEASE_ENTER_DECISION": "请输入决策!",
"MENU": "菜单",
+ "SELECT_PROXY": "选择代理",
+ "ENABLE_PROXY": "启用代理",
+ "PROXY_DISABLED": "代理禁用",
+ "PROXY_ENABLED": "代理启用",
+ "BROKER_OVERVIEW": "Broker概览",
+ "TOTAL_MSG_RECEIVED_TODAY": "今天接收的总消息数",
},
en: {
"DEFAULT": "Default",
@@ -534,6 +540,13 @@ export const translations = {
"ENTER_IP_HINT": "Please enter IP address, press Enter to add.
Supports IPv4, IPv6, and CIDR.",
"PLEASE_ENTER_DECISION": "Please enter decision!",
"MENU": "Menu",
+ "SELECT_PROXY": "Select Proxy",
+ "ENABLE_PROXY": "Enable Proxy",
+ "PROXY_DISABLED": "Proxy Disabled",
+ "PROXY_ENABLED": "Proxy Enabled",
+ "BROKER_OVERVIEW": "Broker Overview",
+ "TOTAL_MSG_RECEIVED_TODAY": "Total messages received today",
+
}
diff --git a/frontend-new/src/pages/Consumer/consumer.jsx
b/frontend-new/src/pages/Consumer/consumer.jsx
index 63d461e..f16b12f 100644
--- a/frontend-new/src/pages/Consumer/consumer.jsx
+++ b/frontend-new/src/pages/Consumer/consumer.jsx
@@ -16,7 +16,7 @@
*/
import React, {useCallback, useEffect, useState} from 'react';
-import {Button, Checkbox, Input, message, notification, Spin, Table} from
'antd';
+import {Button, Checkbox, Input, message, notification, Select, Spin, Switch,
Table} from 'antd';
import {useLanguage} from '../../i18n/LanguageContext';
import {remoteApi} from '../../api/remoteApi/remoteApi';
import ClientInfoModal from "../../components/consumer/ClientInfoModal";
@@ -46,6 +46,27 @@ const ConsumerGroupList = () => {
const [messageApi, msgContextHolder] = message.useMessage();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
+ const [proxyEnabled, setProxyEnabled] = useState(() => {
+ try {
+ const storedValue = localStorage.getItem('proxyEnabled');
+ return storedValue ? JSON.parse(storedValue) : false;
+ } catch (error) {
+ console.error("Failed to read proxyEnabled from localStorage:",
error);
+ return false;
+ }
+ });
+
+ const [selectedProxy, setSelectedProxy] = useState(() => {
+ try {
+ const storedValue = localStorage.getItem('selectedProxy');
+ return storedValue || undefined;
+ } catch (error) {
+ console.error("Failed to read selectedProxy from localStorage:",
error);
+ return undefined;
+ }
+ });
+
+ const [proxyOptions ,setProxyOptions]= useState([]);
const [paginationConf, setPaginationConf] = useState({
current: 1,
pageSize: 10,
@@ -60,7 +81,12 @@ const ConsumerGroupList = () => {
const loadConsumerGroups = useCallback(async (currentPage) => {
setLoading(true);
try {
- const response = await remoteApi.queryConsumerGroupList(false);
+ var response;
+ if(!proxyEnabled){
+ response = await remoteApi.queryConsumerGroupList(false);
+ }else{
+ response = await remoteApi.queryConsumerGroupList(false,
selectedProxy);
+ }
if (response.status === 0) {
setAllConsumerGroupList(response.data);
if (currentPage != null) {
@@ -87,7 +113,6 @@ const ConsumerGroupList = () => {
};
const filterList = useCallback((currentPage, data) => {
- // 排序处理
let sortedData = [...data];
if (sortConfig.sortKey) {
sortedData.sort((a, b) => {
@@ -153,6 +178,48 @@ const ConsumerGroupList = () => {
filterList(paginationConf.current, sortedList);
}, [sortConfig, allConsumerGroupList, paginationConf.current]);
+ const fetchProxyList = useCallback(async () => {
+ remoteApi.queryProxyHomePage((resp) => {
+ setLoading(false);
+ if (resp.status === 0) {
+ const {proxyAddrList, currentProxyAddr} = resp.data;
+ const options = proxyAddrList.map(proxyAddress => ({
+ label: proxyAddress,
+ value: proxyAddress,
+ }));
+ setProxyOptions(options || []);
+ setSelectedProxy(prevSelectedProxy => {
+ if (prevSelectedProxy) {
+ return prevSelectedProxy;
+ }
+ if (options.length > 0) {
+ return options[0].value;
+ }
+ return undefined;
+ });
+ } else {
+ notificationApi.error({message: resp.errMsg ||
t.FETCH_PROXY_LIST_FAILED, duration: 2});
+ }
+ });
+ }, [t]);
+
+ useEffect(() => {
+ localStorage.setItem('proxyEnabled', JSON.stringify(proxyEnabled));
+ }, [proxyEnabled]);
+
+ useEffect(() => {
+ if (selectedProxy) {
+ localStorage.setItem('selectedProxy', selectedProxy);
+ } else {
+ localStorage.removeItem('selectedProxy');
+ }
+ }, [selectedProxy]);
+
+
+ useEffect(() => {
+ fetchProxyList();
+ }, []);
+
useEffect(() => {
loadConsumerGroups();
}, [loadConsumerGroups]);
@@ -389,16 +456,18 @@ const ConsumerGroupList = () => {
<>
{msgContextHolder}
{notificationContextHolder}
- <div style={{padding: '20px'}}>
+ <div style={{ padding: '20px' }}>
<Spin spinning={loading} tip={t.LOADING}>
- <div style={{marginBottom: '20px'}}>
- <div style={{display: 'flex', alignItems: 'center',
gap: '15px'}}>
- <div style={{display: 'flex', alignItems:
'center'}}>
- <label style={{marginRight:
'8px'}}>{t.SUBSCRIPTION_GROUP}:</label>
+ <div style={{ marginBottom: '20px', display: 'flex',
justifyContent: 'space-between', alignItems: 'center' }}>
+ {/* 左侧:筛选和操作按钮 */}
+ <div style={{ display: 'flex', alignItems: 'center',
gap: '15px', flexWrap: 'wrap' }}>
+ <div style={{ display: 'flex', alignItems:
'center' }}>
+ <label style={{ marginRight: '8px',
whiteSpace: 'nowrap' }}>{t.SUBSCRIPTION_GROUP}:</label>
<Input
- style={{width: '200px'}}
+ style={{ width: '200px' }}
value={filterStr}
onChange={(e) =>
handleFilterInputChange(e.target.value)}
+ placeholder="输入订阅组名称"
/>
</div>
<Checkbox checked={filterNormal}
@@ -423,12 +492,35 @@ const ConsumerGroupList = () => {
<Button type="primary"
onClick={handleRefreshConsumerData}>
{t.REFRESH}
</Button>
- {/*<Switch*/}
- {/* checked={intervalProcessSwitch}*/}
- {/* onChange={(checked) =>
setIntervalProcessSwitch(checked)}*/}
- {/* checkedChildren={t.AUTO_REFRESH}*/}
- {/* unCheckedChildren={t.AUTO_REFRESH}*/}
- {/*/>*/}
+ </div>
+
+ {/* 右侧:代理选项 */}
+ <div style={{ display: 'flex', alignItems: 'center',
gap: '15px' }}>
+ <label style={{ marginRight: '8px', whiteSpace:
'nowrap' }}>{t.SELECT_PROXY}:</label>
+ <Select
+ style={{ width: '220px' }}
+ placeholder={t.SELECT_PROXY}
+ onChange={(value) => setSelectedProxy(value)}
+ value={selectedProxy}
+ options={proxyOptions}
+ disabled={!proxyEnabled}
+ allowClear
+ />
+ <label style={{ marginRight: '8px', whiteSpace:
'nowrap' }}>{t.ENABLE_PROXY}:</label>
+ <Switch
+ checked={proxyEnabled}
+ onChange={(checked) => {
+ setProxyEnabled(checked);
+ if (!checked) {
+ setSelectedProxy(undefined);
+ messageApi.info(t.PROXY_DISABLED);
+ } else {
+ messageApi.info(t.PROXY_ENABLED);
+ }
+ }}
+ checkedChildren={t.ENABLED}
+ unCheckedChildren={t.DISABLED}
+ />
</div>
</div>
@@ -443,6 +535,7 @@ const ConsumerGroupList = () => {
/>
</Spin>
+ {/* 模态框组件保持不变 */}
<ClientInfoModal
visible={showClientInfo}
group={selectedGroup}
diff --git a/frontend-new/src/pages/Dashboard/DashboardPage.jsx
b/frontend-new/src/pages/Dashboard/DashboardPage.jsx
index 8161a5d..d0c5ee4 100644
--- a/frontend-new/src/pages/Dashboard/DashboardPage.jsx
+++ b/frontend-new/src/pages/Dashboard/DashboardPage.jsx
@@ -250,17 +250,18 @@ const DashboardPage = () => {
const brokerAddrTable = resp.data.clusterInfo.brokerAddrTable;
// Corrected to brokerAddrTable
const brokerDetail = resp.data.brokerServer;
const clusterMap = tools.generateBrokerMap(brokerDetail,
clusterAddrTable, brokerAddrTable);
-
+ console.log(brokerAddrTable)
let brokerArray = [];
Object.values(clusterMap).forEach(brokersInCluster => {
brokerArray = brokerArray.concat(brokersInCluster);
});
- // Update broker table data
- setBrokerTableData(brokerArray.map(broker => ({
+ const newData = brokerArray.map(broker => ({
...broker,
- key: broker.brokerName // Ant Design Table needs a unique
key
- })));
+ key: broker.brokerName,
+ }));
+ console.log("即将设置的数据:", newData); // 先打印
+ setBrokerTableData(newData); // 再设置状态
brokerArray.sort((firstBroker, lastBroker) => {
const firstTotalMsg =
parseFloat(firstBroker.msgGetTotalTodayNow || 0);
@@ -347,7 +348,7 @@ const DashboardPage = () => {
const brokerColumns = [
{title: t.BROKER_NAME, dataIndex: 'brokerName', key: 'brokerName'},
- {title: t.BROKER_ADDR, dataIndex: 'brokerAddress', key:
'brokerAddress'},
+ {title: t.BROKER_ADDR, dataIndex: 'address', key: 'address'},
{
title: t.TOTAL_MSG_RECEIVED_TODAY,
dataIndex: 'msgGetTotalTodayNow',
diff --git a/frontend-new/src/pages/DlqMessage/dlqmessage.jsx
b/frontend-new/src/pages/DlqMessage/dlqmessage.jsx
index 2591656..b74ee30 100644
--- a/frontend-new/src/pages/DlqMessage/dlqmessage.jsx
+++ b/frontend-new/src/pages/DlqMessage/dlqmessage.jsx
@@ -179,7 +179,6 @@ const DlqMessageQueryPage = () => {
return;
}
setLoading(true);
- // console.log("根据Message ID查询DLQ消息:", { msgId: messageId,
consumerGroup: selectedConsumerGroup });
try {
const resp = await remoteApi.viewMessage(messageId,
DLQ_GROUP_TOPIC_PREFIX + selectedConsumerGroup);
if (resp.status === 0) {
@@ -323,7 +322,6 @@ const DlqMessageQueryPage = () => {
msgId: message.properties.ORIGIN_MESSAGE_ID,
consumerGroup: selectedConsumerGroup,
}));
- // console.log(`批量重发DLQ消息到 ${selectedConsumerGroup}:`,
messagesToResend);
try {
const resp = await
remoteApi.batchResendDlqMessage(messagesToResend);
if (resp.status === 0) {
@@ -355,7 +353,6 @@ const DlqMessageQueryPage = () => {
message: t.ERROR,
description: t.BATCH_RESEND_FAILED,
});
- console.error("批量重发失败:", error);
} finally {
setLoading(false);
}
diff --git a/frontend-new/src/pages/Message/message.jsx
b/frontend-new/src/pages/Message/message.jsx
index 73d3baf..323a271 100644
--- a/frontend-new/src/pages/Message/message.jsx
+++ b/frontend-new/src/pages/Message/message.jsx
@@ -146,7 +146,6 @@ const MessageQueryPage = () => {
message: t.ERROR,
description: t.QUERY_FAILED,
});
- console.error("查询失败:", error);
} finally {
setLoading(false);
}
@@ -182,7 +181,6 @@ const MessageQueryPage = () => {
message: t.ERROR,
description: t.QUERY_FAILED,
});
- console.error("查询失败:", error);
} finally {
setLoading(false);
}
@@ -241,7 +239,6 @@ const MessageQueryPage = () => {
message: t.ERROR,
description: t.RESEND_FAILED,
});
- console.error("重发失败:", error);
} finally {
setLoading(false);
// Optionally, you might want to refresh the message detail after
resend
@@ -455,7 +452,6 @@ const MessageQueryPage = () => {
</Button>
</Form.Item>
</Form>
- {/* Message ID 查询结果通常直接弹窗显示,这里不需要表格 */}
</div>
</TabPane>
</Tabs>