Skip to content

Commit 52545cc

Browse files
authored
[GSOC][RIP-78][ISSUES#308] Add part of refactored front-end files (#310)
1 parent b75ace4 commit 52545cc

File tree

8 files changed

+990
-0
lines changed

8 files changed

+990
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
export const defaultTheme = {
18+
token: {
19+
colorPrimary: '#0cb5fb', // 主题色
20+
borderRadius: 1.5, // 组件圆角
21+
},
22+
components: {
23+
Button: {
24+
colorPrimary: '#1c324a', // 普通按钮主题色
25+
},
26+
Layout: {
27+
headerBg: '#1c324a', // 设置 Header 的背景色为 #1c324a
28+
headerColor: '#ffffff', // 设置 Header 内文本颜色为白色
29+
backgroundColor: '#ffffff', // 设置 Layout 的背景色为白色
30+
colorBgLayout: '#f9fcfe',
31+
},
32+
Menu: {
33+
darkItemBg: '#1c324a',
34+
horizontalItemSelectedBg: '#0cb5fb',
35+
itemSelectedColor: '#ffffff',
36+
itemColor: '#ffffff',
37+
colorText: 'rgba(255, 255, 255, 0.88)', // Adjust for dark theme menu
38+
activeBarBorderWidth: 0,
39+
},
40+
Drawer: {
41+
colorBgElevated: '#1c324a', // Drawer 背景色
42+
},
43+
},
44+
};
45+
46+
export const pinkTheme = {
47+
token: {
48+
colorPrimary: '#FF69B4', // 热粉色
49+
borderRadius: 1.5,
50+
},
51+
components: {
52+
Button: {
53+
colorPrimary: '#FFC0CB', // 深粉色
54+
},
55+
Layout: {
56+
headerBg: '#FFC0CB', // 粉色
57+
headerColor: '#000000', // 黑色文本
58+
backgroundColor: '#F8F8FF', // 幽灵白
59+
colorBgLayout: '#faf4f4',
60+
},
61+
Menu: {
62+
darkItemBg: '#FFC0CB', // 粉色
63+
horizontalItemSelectedBg: '#FF69B4',
64+
itemSelectedColor: '#ffffff',
65+
itemColor: '#000000', // 黑色文本
66+
colorText: 'rgba(0, 0, 0, 0.88)',
67+
activeBarBorderWidth: 0,
68+
},
69+
Drawer: {
70+
colorBgElevated: '#FFC0CB', // Drawer 背景色
71+
},
72+
},
73+
};
74+
75+
export const greenTheme = {
76+
token: {
77+
colorPrimary: '#52c41a', // 绿色
78+
borderRadius: 1.5,
79+
},
80+
components: {
81+
Button: {
82+
colorPrimary: '#7cb305', // 橄榄绿
83+
},
84+
Layout: {
85+
headerBg: '#3f673f', // 深绿色
86+
headerColor: '#ffffff', // 白色文本
87+
backgroundColor: '#f6ffed',
88+
colorBgLayout: '#ebf8eb',
89+
},
90+
Menu: {
91+
darkItemBg: '#3f673f', // 深绿色
92+
horizontalItemSelectedBg: '#52c41a',
93+
itemSelectedColor: '#ffffff',
94+
itemColor: '#ffffff',
95+
colorText: 'rgba(255, 255, 255, 0.88)',
96+
activeBarBorderWidth: 0,
97+
},
98+
Drawer: {
99+
colorBgElevated: '#3f673f', // Drawer 背景色
100+
},
101+
},
102+
};
103+
104+
export const themes = {
105+
default: defaultTheme,
106+
pink: pinkTheme,
107+
green: greenTheme,
108+
};
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 from 'react';
19+
import { Form, Input, Typography, Modal } from 'antd';
20+
import moment from 'moment';
21+
import { useLanguage } from '../i18n/LanguageContext'; // 根据实际路径调整
22+
23+
const { Text } = Typography;
24+
25+
const DlqMessageDetailViewDialog = ({ ngDialogData }) => {
26+
const { t } = useLanguage();
27+
28+
const messageView = ngDialogData?.messageView || {};
29+
30+
return (
31+
<div style={{ padding: '20px' }}>
32+
<Form layout="horizontal" labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
33+
<Form.Item label="Message ID:">
34+
<Text strong>{messageView.msgId}</Text>
35+
</Form.Item>
36+
<Form.Item label="Topic:">
37+
<Text strong>{messageView.topic}</Text>
38+
</Form.Item>
39+
<Form.Item label="Properties:">
40+
<Input.TextArea
41+
value={typeof messageView.properties === 'object' ? JSON.stringify(messageView.properties, null, 2) : messageView.properties}
42+
style={{ minHeight: 100, resize: 'none' }}
43+
readOnly
44+
/>
45+
</Form.Item>
46+
<Form.Item label="ReconsumeTimes:">
47+
<Text strong>{messageView.reconsumeTimes}</Text>
48+
</Form.Item>
49+
<Form.Item label="Tag:">
50+
<Text strong>{messageView.properties?.TAGS}</Text>
51+
</Form.Item>
52+
<Form.Item label="Key:">
53+
<Text strong>{messageView.properties?.KEYS}</Text>
54+
</Form.Item>
55+
<Form.Item label="Storetime:">
56+
<Text strong>{moment(messageView.storeTimestamp).format('YYYY-MM-DD HH:mm:ss')}</Text>
57+
</Form.Item>
58+
<Form.Item label="StoreHost:">
59+
<Text strong>{messageView.storeHost}</Text>
60+
</Form.Item>
61+
<Form.Item label="Message body:">
62+
<Input.TextArea
63+
value={messageView.messageBody}
64+
style={{ minHeight: 100, resize: 'none' }}
65+
readOnly
66+
/>
67+
</Form.Item>
68+
</Form>
69+
</div>
70+
);
71+
};
72+
73+
export default DlqMessageDetailViewDialog;
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
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 from 'react';
19+
import { Modal, Button, Typography, Descriptions, Tag, Spin, notification } from 'antd';
20+
import moment from 'moment';
21+
import { ExclamationCircleOutlined, SyncOutlined } from '@ant-design/icons';
22+
import { useLanguage } from '../i18n/LanguageContext';
23+
import { remoteApi } from '../api/remoteApi/remoteApi'; // 确保这个路径正确
24+
25+
const { Text, Paragraph } = Typography;
26+
27+
const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResendMessage }) => {
28+
const { t } = useLanguage();
29+
const [loading, setLoading] = React.useState(true);
30+
const [messageDetail, setMessageDetail] = React.useState(null);
31+
const [error, setError] = React.useState(null);
32+
33+
React.useEffect(() => {
34+
const fetchMessageDetail = async () => {
35+
// 只有当 visible 为 true 且 messageId 和 topic 存在时才进行数据请求
36+
if (!visible || !messageId || !topic) {
37+
// 如果 Modal 不可见或者必要参数缺失,则不加载数据
38+
setMessageDetail(null); // 清空旧数据
39+
setError(null); // 清空错误信息
40+
setLoading(false); // 停止加载状态
41+
return;
42+
}
43+
44+
setLoading(true);
45+
setError(null); // 在每次新的请求前清除之前的错误
46+
try {
47+
const resp = await remoteApi.viewMessage(messageId, topic);
48+
if (resp.status === 0) {
49+
setMessageDetail(resp.data);
50+
} else {
51+
const errorMessage = resp.errMsg || t.FETCH_MESSAGE_DETAIL_FAILED;
52+
setError(errorMessage);
53+
notification.error({
54+
message: t.ERROR,
55+
description: errorMessage,
56+
});
57+
}
58+
} catch (err) {
59+
const errorMessage = t.FETCH_MESSAGE_DETAIL_FAILED;
60+
setError(errorMessage);
61+
notification.error({
62+
message: t.ERROR,
63+
description: errorMessage,
64+
});
65+
console.error("Error fetching message detail:", err);
66+
} finally {
67+
setLoading(false);
68+
}
69+
};
70+
71+
fetchMessageDetail();
72+
}, [visible, messageId, topic, t]); // 依赖项中添加 visible,确保在 Modal 显示时触发
73+
74+
75+
// handleShowExceptionDesc 方法不再需要,因为我们直接使用 Paragraph 的 ellipsis
76+
77+
return (
78+
<Modal
79+
title={t.MESSAGE_DETAIL}
80+
open={visible} // Ant Design 5.x 版本中,visible 属性已更名为 open
81+
onCancel={onCancel}
82+
footer={[
83+
<Button key="close" onClick={onCancel}>
84+
{t.CLOSE}
85+
</Button>,
86+
]}
87+
width={900}
88+
destroyOnHidden={true} // 使用新的 destroyOnHidden 替代 destroyOnClose
89+
>
90+
<Spin spinning={loading} tip={t.LOADING}>
91+
{error && (
92+
<Paragraph type="danger" style={{ textAlign: 'center' }}>
93+
{error}
94+
</Paragraph>
95+
)}
96+
{messageDetail ? ( // 确保 messageDetail 存在时才渲染内容
97+
<>
98+
{/* 消息信息部分 */}
99+
<Descriptions title={<Text strong>{t.MESSAGE_INFO}</Text>} bordered column={2} size="small" style={{ marginBottom: 20 }}>
100+
<Descriptions.Item label="Topic" span={2}><Text copyable>{messageDetail.messageView.topic}</Text></Descriptions.Item>
101+
<Descriptions.Item label="Message ID" span={2}><Text copyable>{messageDetail.messageView.msgId}</Text></Descriptions.Item>
102+
<Descriptions.Item label="StoreHost">{messageDetail.messageView.storeHost}</Descriptions.Item>
103+
<Descriptions.Item label="BornHost">{messageDetail.messageView.bornHost}</Descriptions.Item>
104+
<Descriptions.Item label="StoreTime">
105+
{moment(messageDetail.messageView.storeTimestamp).format("YYYY-MM-DD HH:mm:ss")}
106+
</Descriptions.Item>
107+
<Descriptions.Item label="BornTime">
108+
{moment(messageDetail.messageView.bornTimestamp).format("YYYY-MM-DD HH:mm:ss")}
109+
</Descriptions.Item>
110+
<Descriptions.Item label="Queue ID">{messageDetail.messageView.queueId}</Descriptions.Item>
111+
<Descriptions.Item label="Queue Offset">{messageDetail.messageView.queueOffset}</Descriptions.Item>
112+
<Descriptions.Item label="StoreSize">{messageDetail.messageView.storeSize} bytes</Descriptions.Item>
113+
<Descriptions.Item label="ReconsumeTimes">{messageDetail.messageView.reconsumeTimes}</Descriptions.Item>
114+
<Descriptions.Item label="BodyCRC">{messageDetail.messageView.bodyCRC}</Descriptions.Item>
115+
<Descriptions.Item label="SysFlag">{messageDetail.messageView.sysFlag}</Descriptions.Item>
116+
<Descriptions.Item label="Flag">{messageDetail.messageView.flag}</Descriptions.Item>
117+
<Descriptions.Item label="PreparedTransactionOffset">{messageDetail.messageView.preparedTransactionOffset}</Descriptions.Item>
118+
</Descriptions>
119+
120+
{/* 消息属性部分 */}
121+
{Object.keys(messageDetail.messageView.properties).length > 0 && (
122+
<Descriptions title={<Text strong>{t.MESSAGE_PROPERTIES}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}>
123+
{Object.entries(messageDetail.messageView.properties).map(([key, value]) => (
124+
<Descriptions.Item label={key} key={key}><Text copyable>{value}</Text></Descriptions.Item>
125+
))}
126+
</Descriptions>
127+
)}
128+
129+
{/* 消息体部分 */}
130+
<Descriptions title={<Text strong>{t.MESSAGE_BODY}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}>
131+
<Descriptions.Item>
132+
<Paragraph
133+
copyable
134+
ellipsis={{
135+
rows: 5,
136+
expandable: true,
137+
symbol: t.SHOW_ALL_CONTENT,
138+
}}
139+
>
140+
{messageDetail.messageView.messageBody}
141+
</Paragraph>
142+
</Descriptions.Item>
143+
</Descriptions>
144+
145+
{/* 消息轨迹列表部分 */}
146+
{messageDetail.messageTrackList && messageDetail.messageTrackList.length > 0 ? (
147+
<>
148+
<Text strong>{t.MESSAGE_TRACKING}</Text>
149+
<div style={{ marginTop: 10 }}>
150+
{messageDetail.messageTrackList.map((track, index) => (
151+
<Descriptions bordered column={1} size="small" key={index} style={{ marginBottom: 15 }}>
152+
<Descriptions.Item label={t.CONSUMER_GROUP}>
153+
{track.consumerGroup}
154+
</Descriptions.Item>
155+
<Descriptions.Item label={t.TRACK_TYPE}>
156+
<Tag color={
157+
track.trackType === 'CONSUMED_SOME_TIME_OK' ? 'success' :
158+
track.trackType === 'NOT_ONLINE' ? 'default' :
159+
track.trackType === 'PULL_SUCCESS' ? 'processing' :
160+
track.trackType === 'NO_MATCHED_CONSUMER' ? 'warning' :
161+
'error'
162+
}>
163+
{track.trackType}
164+
</Tag>
165+
</Descriptions.Item>
166+
<Descriptions.Item label={t.OPERATION}>
167+
<Button
168+
icon={<SyncOutlined />}
169+
onClick={() => onResendMessage(messageDetail.messageView, track.consumerGroup)}
170+
size="small"
171+
style={{ marginRight: 8 }}
172+
>
173+
{t.RESEND_MESSAGE}
174+
</Button>
175+
{/* 移除“查看异常”按钮,因为现在直接在下方展示可展开内容 */}
176+
</Descriptions.Item>
177+
{track.exceptionDesc && (
178+
<Descriptions.Item label={t.EXCEPTION_SUMMARY}>
179+
{/* 异常信息截断显示,点击“查看更多”可展开 */}
180+
<Paragraph
181+
ellipsis={{
182+
rows: 2, // 默认显示2行
183+
expandable: true,
184+
symbol: <Text style={{ color: '#1890ff', cursor: 'pointer' }}>{t.READ_MORE}</Text>, // 蓝色展开文本
185+
}}
186+
>
187+
{track.exceptionDesc}
188+
</Paragraph>
189+
</Descriptions.Item>
190+
)}
191+
</Descriptions>
192+
))}
193+
</div>
194+
</>
195+
) : (
196+
<Paragraph>{t.NO_TRACKING_INFO}</Paragraph>
197+
)}
198+
</>
199+
) : (
200+
// 当 messageDetail 为 null 时,可以显示一个占位符或者不显示内容
201+
!loading && !error && <Paragraph style={{ textAlign: 'center' }}>{t.NO_MESSAGE_DETAIL_AVAILABLE}</Paragraph>
202+
)}
203+
</Spin>
204+
</Modal>
205+
);
206+
};
207+
208+
export default MessageDetailViewDialog;

0 commit comments

Comments
 (0)