[ISSUE #329] Add Frontend Proxy Component Support (#336)

This commit is contained in:
Crazylychee
2025-07-05 20:53:57 +08:00
committed by GitHub
parent 4b9ed97f8f
commit 07793d8aae
6 changed files with 130 additions and 30 deletions

View File

@@ -376,9 +376,9 @@ const remoteApi = {
} }
}, },
queryConsumerGroupList: async (skipSysGroup = false) => { queryConsumerGroupList: async (skipSysGroup, address) => {
try { 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(); const data = await response.json();
return data; return data;
} catch (error) { } catch (error) {

View File

@@ -278,6 +278,12 @@ export const translations = {
"ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR", "ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR",
"PLEASE_ENTER_DECISION": "请输入决策!", "PLEASE_ENTER_DECISION": "请输入决策!",
"MENU": "菜单", "MENU": "菜单",
"SELECT_PROXY": "选择代理",
"ENABLE_PROXY": "启用代理",
"PROXY_DISABLED": "代理禁用",
"PROXY_ENABLED": "代理启用",
"BROKER_OVERVIEW": "Broker概览",
"TOTAL_MSG_RECEIVED_TODAY": "今天接收的总消息数",
}, },
en: { en: {
"DEFAULT": "Default", "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.", "ENTER_IP_HINT": "Please enter IP address, press Enter to add. Supports IPv4, IPv6, and CIDR.",
"PLEASE_ENTER_DECISION": "Please enter decision!", "PLEASE_ENTER_DECISION": "Please enter decision!",
"MENU": "Menu", "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",
} }

View File

@@ -16,7 +16,7 @@
*/ */
import React, {useCallback, useEffect, useState} from 'react'; 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 {useLanguage} from '../../i18n/LanguageContext';
import {remoteApi} from '../../api/remoteApi/remoteApi'; import {remoteApi} from '../../api/remoteApi/remoteApi';
import ClientInfoModal from "../../components/consumer/ClientInfoModal"; import ClientInfoModal from "../../components/consumer/ClientInfoModal";
@@ -46,6 +46,27 @@ const ConsumerGroupList = () => {
const [messageApi, msgContextHolder] = message.useMessage(); const [messageApi, msgContextHolder] = message.useMessage();
const [notificationApi, notificationContextHolder] = notification.useNotification(); 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({ const [paginationConf, setPaginationConf] = useState({
current: 1, current: 1,
pageSize: 10, pageSize: 10,
@@ -60,7 +81,12 @@ const ConsumerGroupList = () => {
const loadConsumerGroups = useCallback(async (currentPage) => { const loadConsumerGroups = useCallback(async (currentPage) => {
setLoading(true); setLoading(true);
try { 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) { if (response.status === 0) {
setAllConsumerGroupList(response.data); setAllConsumerGroupList(response.data);
if (currentPage != null) { if (currentPage != null) {
@@ -87,7 +113,6 @@ const ConsumerGroupList = () => {
}; };
const filterList = useCallback((currentPage, data) => { const filterList = useCallback((currentPage, data) => {
// 排序处理
let sortedData = [...data]; let sortedData = [...data];
if (sortConfig.sortKey) { if (sortConfig.sortKey) {
sortedData.sort((a, b) => { sortedData.sort((a, b) => {
@@ -153,6 +178,48 @@ const ConsumerGroupList = () => {
filterList(paginationConf.current, sortedList); filterList(paginationConf.current, sortedList);
}, [sortConfig, allConsumerGroupList, paginationConf.current]); }, [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(() => { useEffect(() => {
loadConsumerGroups(); loadConsumerGroups();
}, [loadConsumerGroups]); }, [loadConsumerGroups]);
@@ -389,16 +456,18 @@ const ConsumerGroupList = () => {
<> <>
{msgContextHolder} {msgContextHolder}
{notificationContextHolder} {notificationContextHolder}
<div style={{padding: '20px'}}> <div style={{ padding: '20px' }}>
<Spin spinning={loading} tip={t.LOADING}> <Spin spinning={loading} tip={t.LOADING}>
<div style={{marginBottom: '20px'}}> <div style={{ marginBottom: '20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{display: 'flex', alignItems: 'center', gap: '15px'}}> {/* 左侧:筛选和操作按钮 */}
<div style={{display: 'flex', alignItems: 'center'}}> <div style={{ display: 'flex', alignItems: 'center', gap: '15px', flexWrap: 'wrap' }}>
<label style={{marginRight: '8px'}}>{t.SUBSCRIPTION_GROUP}:</label> <div style={{ display: 'flex', alignItems: 'center' }}>
<label style={{ marginRight: '8px', whiteSpace: 'nowrap' }}>{t.SUBSCRIPTION_GROUP}:</label>
<Input <Input
style={{width: '200px'}} style={{ width: '200px' }}
value={filterStr} value={filterStr}
onChange={(e) => handleFilterInputChange(e.target.value)} onChange={(e) => handleFilterInputChange(e.target.value)}
placeholder="输入订阅组名称"
/> />
</div> </div>
<Checkbox checked={filterNormal} <Checkbox checked={filterNormal}
@@ -423,12 +492,35 @@ const ConsumerGroupList = () => {
<Button type="primary" onClick={handleRefreshConsumerData}> <Button type="primary" onClick={handleRefreshConsumerData}>
{t.REFRESH} {t.REFRESH}
</Button> </Button>
{/*<Switch*/} </div>
{/* checked={intervalProcessSwitch}*/}
{/* onChange={(checked) => setIntervalProcessSwitch(checked)}*/} {/* 右侧:代理选项 */}
{/* checkedChildren={t.AUTO_REFRESH}*/} <div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
{/* unCheckedChildren={t.AUTO_REFRESH}*/} <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>
</div> </div>
@@ -443,6 +535,7 @@ const ConsumerGroupList = () => {
/> />
</Spin> </Spin>
{/* 模态框组件保持不变 */}
<ClientInfoModal <ClientInfoModal
visible={showClientInfo} visible={showClientInfo}
group={selectedGroup} group={selectedGroup}

View File

@@ -250,17 +250,18 @@ const DashboardPage = () => {
const brokerAddrTable = resp.data.clusterInfo.brokerAddrTable; // Corrected to brokerAddrTable const brokerAddrTable = resp.data.clusterInfo.brokerAddrTable; // Corrected to brokerAddrTable
const brokerDetail = resp.data.brokerServer; const brokerDetail = resp.data.brokerServer;
const clusterMap = tools.generateBrokerMap(brokerDetail, clusterAddrTable, brokerAddrTable); const clusterMap = tools.generateBrokerMap(brokerDetail, clusterAddrTable, brokerAddrTable);
console.log(brokerAddrTable)
let brokerArray = []; let brokerArray = [];
Object.values(clusterMap).forEach(brokersInCluster => { Object.values(clusterMap).forEach(brokersInCluster => {
brokerArray = brokerArray.concat(brokersInCluster); brokerArray = brokerArray.concat(brokersInCluster);
}); });
// Update broker table data const newData = brokerArray.map(broker => ({
setBrokerTableData(brokerArray.map(broker => ({
...broker, ...broker,
key: broker.brokerName // Ant Design Table needs a unique key key: broker.brokerName,
}))); }));
console.log("即将设置的数据:", newData); // 先打印
setBrokerTableData(newData); // 再设置状态
brokerArray.sort((firstBroker, lastBroker) => { brokerArray.sort((firstBroker, lastBroker) => {
const firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow || 0); const firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow || 0);
@@ -347,7 +348,7 @@ const DashboardPage = () => {
const brokerColumns = [ const brokerColumns = [
{title: t.BROKER_NAME, dataIndex: 'brokerName', key: 'brokerName'}, {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, title: t.TOTAL_MSG_RECEIVED_TODAY,
dataIndex: 'msgGetTotalTodayNow', dataIndex: 'msgGetTotalTodayNow',

View File

@@ -179,7 +179,6 @@ const DlqMessageQueryPage = () => {
return; return;
} }
setLoading(true); setLoading(true);
// console.log("根据Message ID查询DLQ消息:", { msgId: messageId, consumerGroup: selectedConsumerGroup });
try { try {
const resp = await remoteApi.viewMessage(messageId, DLQ_GROUP_TOPIC_PREFIX + selectedConsumerGroup); const resp = await remoteApi.viewMessage(messageId, DLQ_GROUP_TOPIC_PREFIX + selectedConsumerGroup);
if (resp.status === 0) { if (resp.status === 0) {
@@ -323,7 +322,6 @@ const DlqMessageQueryPage = () => {
msgId: message.properties.ORIGIN_MESSAGE_ID, msgId: message.properties.ORIGIN_MESSAGE_ID,
consumerGroup: selectedConsumerGroup, consumerGroup: selectedConsumerGroup,
})); }));
// console.log(`批量重发DLQ消息到 ${selectedConsumerGroup}:`, messagesToResend);
try { try {
const resp = await remoteApi.batchResendDlqMessage(messagesToResend); const resp = await remoteApi.batchResendDlqMessage(messagesToResend);
if (resp.status === 0) { if (resp.status === 0) {
@@ -355,7 +353,6 @@ const DlqMessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.BATCH_RESEND_FAILED, description: t.BATCH_RESEND_FAILED,
}); });
console.error("批量重发失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
} }

View File

@@ -146,7 +146,6 @@ const MessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.QUERY_FAILED, description: t.QUERY_FAILED,
}); });
console.error("查询失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -182,7 +181,6 @@ const MessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.QUERY_FAILED, description: t.QUERY_FAILED,
}); });
console.error("查询失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@@ -241,7 +239,6 @@ const MessageQueryPage = () => {
message: t.ERROR, message: t.ERROR,
description: t.RESEND_FAILED, description: t.RESEND_FAILED,
}); });
console.error("重发失败:", error);
} finally { } finally {
setLoading(false); setLoading(false);
// Optionally, you might want to refresh the message detail after resend // Optionally, you might want to refresh the message detail after resend
@@ -455,7 +452,6 @@ const MessageQueryPage = () => {
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
{/* Message ID 查询结果通常直接弹窗显示,这里不需要表格 */}
</div> </div>
</TabPane> </TabPane>
</Tabs> </Tabs>