This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 0e5d2ca5a fix(web): guard topic consumer pagination race and stale
metrics datasource, wire dead buttons (#1992)
0e5d2ca5a is described below
commit 0e5d2ca5a3e083214443636ac2c02137050324e8
Author: btlqql <[email protected]>
AuthorDate: Tue Aug 18 17:04:59 2026 +0800
fix(web): guard topic consumer pagination race and stale metrics
datasource, wire dead buttons (#1992)
Co-authored-by: yyqdbngt <[email protected]>
---
web/src/components/MetricsExplorer.tsx | 3 +++
web/src/pages/ai/index.tsx | 13 +++++++++++--
web/src/pages/instance/consumer.tsx | 2 ++
web/src/pages/instance/topic.tsx | 7 ++++++-
4 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/web/src/components/MetricsExplorer.tsx
b/web/src/components/MetricsExplorer.tsx
index 627dac61e..b9207c2cf 100644
--- a/web/src/components/MetricsExplorer.tsx
+++ b/web/src/components/MetricsExplorer.tsx
@@ -490,6 +490,9 @@ const MetricsExplorer = ({ instanceId }:
MetricsExplorerProps) => {
useEffect(() => {
if (dataSourceKey && !availableDataSources.some((source) => source.key ===
dataSourceKey)) {
window.setTimeout(() => {
+ // Keep the ref in sync with the state; queries read the ref, so a
stale key would
+ // keep hitting the de-registered data source while the UI shows the
default.
+ dataSourceKeyRef.current = '';
setDataSourceKey('');
setData(null);
}, 0);
diff --git a/web/src/pages/ai/index.tsx b/web/src/pages/ai/index.tsx
index 6b0041c0f..8f02ccbd3 100644
--- a/web/src/pages/ai/index.tsx
+++ b/web/src/pages/ai/index.tsx
@@ -390,6 +390,7 @@ const AiPage = () => {
const [modelsLoading, setModelsLoading] = useState(false);
const [selectedModel, setSelectedModel] = useState('');
const [toolModalOpen, setToolModalOpen] = useState(false);
+ const [enhance, setEnhance] = useState(false);
const [tools, setTools] = useState<McpTool[]>([]);
const [toolsLoading, setToolsLoading] = useState(false);
const [clusterOptions, setClusterOptions] = useState<{ value: string; label:
string }[]>([]);
@@ -850,7 +851,15 @@ const AiPage = () => {
<SlidersHorizontal size={17} />
<span>工具</span>
</button>
- <button className="tool-btn">
+ <button
+ className="tool-btn"
+ onClick={() => setEnhance((value) => !value)}
+ style={{
+ borderColor: enhance ? '#1677ff' : undefined,
+ color: enhance ? '#1677ff' : undefined,
+ }}
+ title="发送前增强 Prompt"
+ >
<Sparkle size={17} />
<span>Prompt 增强</span>
</button>
@@ -859,7 +868,7 @@ const AiPage = () => {
<div className="shrink-0 flex items-center gap-1">
<button
className="flex items-center justify-center w-9 h-9
rounded-full bg-gradient-to-r from-purple-500 to-violet-600 text-white
shadow-lg hover:shadow-xl transition-all hover:scale-105"
- onClick={() => void handleSend()}
+ onClick={() => void handleSend(undefined, undefined,
enhance)}
disabled={loading || !inputValue.trim() || !llmReady}
style={{
opacity: loading || !inputValue.trim() || !llmReady ?
0.5 : 1,
diff --git a/web/src/pages/instance/consumer.tsx
b/web/src/pages/instance/consumer.tsx
index 095400877..d37625c20 100644
--- a/web/src/pages/instance/consumer.tsx
+++ b/web/src/pages/instance/consumer.tsx
@@ -685,6 +685,8 @@ const ConsumerPageContent = ({
<Button
size="small"
icon={<Eye size={14} />}
+ disabled
+ title="队列分布暂未支持"
style={{ borderColor: '#1677ff', color: '#1677ff' }}
>
查看分布
diff --git a/web/src/pages/instance/topic.tsx b/web/src/pages/instance/topic.tsx
index 09313851e..83be3de7c 100644
--- a/web/src/pages/instance/topic.tsx
+++ b/web/src/pages/instance/topic.tsx
@@ -335,6 +335,7 @@ const TopicPage = () => {
const topicRequestIdRef = useRef(0);
const detailRequestIdRef = useRef(0);
+ const consumersRequestIdRef = useRef(0);
const createInFlightRef = useRef(false);
useEffect(() => {
@@ -392,13 +393,17 @@ const TopicPage = () => {
};
const loadTopicConsumers = async (topic: Topic, page = 1, pageSize = 20) => {
+ const requestId = ++consumersRequestIdRef.current;
const consumers = await getTopicConsumerPage(
topic.name,
selectedInstanceId || undefined,
page,
pageSize,
);
- setConsumersByTopic((previous) => ({ ...previous, [topic.name]: consumers
}));
+ // Guard against a slower earlier page overwriting a newer one when the
user pages quickly.
+ if (requestId === consumersRequestIdRef.current) {
+ setConsumersByTopic((previous) => ({ ...previous, [topic.name]:
consumers }));
+ }
};
// ─── Open detail modal ────────────────────────────────────────