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>

Reply via email to