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 3a443e9a1 fix(web): Segmented group type in create modal and 
equal-height audit cards (#7367)
3a443e9a1 is described below

commit 3a443e9a15015b5290cf6533568c3de1606d9e07
Author: lizhimins <[email protected]>
AuthorDate: Thu Oct 29 15:20:41 2026 +0800

    fix(web): Segmented group type in create modal and equal-height audit cards 
(#7367)
    
    Create Group modal: the message-type Select offered delay/transaction types
    that do not apply to consumer groups, plus a delivery-order Select with a
    global order the backends do not support. Use the same Segmented control as
    the create-topic modal with only NORMAL/FIFO, and derive deliveryOrderType
    from the chosen type so ordered groups still reach the broker as orderly.
    
    Audit page: the summary statistic cards sized to their content, leaving the
    success-rate card (one extra detail line) 44px taller than its row siblings.
    Stretch every card in the summary and risk-insight rows (Row align="stretch"
    plus Card height 100%) so each visual row is even.
---
 .../pages/instance/__tests__/ConsumerPage.test.tsx | 24 +++++-----
 web/src/pages/instance/consumer.tsx                | 52 ++++++++--------------
 web/src/pages/ops/AuditRiskInsights.tsx            |  6 +--
 web/src/pages/ops/AuditSummaryCards.tsx            | 14 +++---
 4 files changed, 39 insertions(+), 57 deletions(-)

diff --git a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx 
b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
index c7fd83a45..bd8a2f406 100644
--- a/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
+++ b/web/src/pages/instance/__tests__/ConsumerPage.test.tsx
@@ -643,7 +643,7 @@ describe('Consumer page', () => {
     expect(await screen.findByText('资源命名与归属规则')).toBeInTheDocument();
   });
 
-  it('submits the canonical global delivery order type', async () => {
+  it('submits the partition-ordered delivery type for an ordered group', async 
() => {
     const user = userEvent.setup({ pointerEventsCheck: 0 });
     const confirmSpy = vi.spyOn(Modal, 'confirm').mockImplementation((config) 
=> {
       void config.onOk?.();
@@ -656,27 +656,23 @@ describe('Consumer page', () => {
     await waitFor(() => expect(createButton).toBeEnabled());
     await user.click(createButton);
     const dialog = await screen.findByRole('dialog');
-    await user.type(within(dialog).getByLabelText('Group 名称'), 
'cg-global-orders');
+    await user.type(within(dialog).getByLabelText('Group 名称'), 'cg-orderly');
 
-    const dataTypeSelect = within(dialog).getByRole('combobox', { name: 
'订阅组类型' });
-    fireEvent.mouseDown(dataTypeSelect.parentElement!);
-    await user.click(
-      await screen.findByText('顺序消息', { selector: 
'.ant-select-item-option-content' }),
-    );
+    // 订阅组类型与创建 Topic 共用 Segmented,只提供普通/顺序两档,顺序组固定分区顺序
+    expect(within(dialog).queryByText('延迟消息')).not.toBeInTheDocument();
+    expect(within(dialog).queryByText('事务消息')).not.toBeInTheDocument();
+    await user.click(within(dialog).getByRole('radio', { name: '顺序消息' }));
+    expect(within(dialog).queryByRole('combobox', { name: '顺序类型' 
})).not.toBeInTheDocument();
+    expect(within(dialog).queryByText('全局顺序')).not.toBeInTheDocument();
 
-    const orderTypeSelect = within(dialog).getByRole('combobox', { name: 
'顺序类型' });
-    fireEvent.mouseDown(orderTypeSelect.parentElement!);
-    await user.click(
-      await screen.findByText('全局顺序', { selector: 
'.ant-select-item-option-content' }),
-    );
     await user.click(within(dialog).getByRole('button', { name: /创\s*建/ }));
 
     await waitFor(() =>
       expect(consumerService.createConsumerGroup).toHaveBeenCalledWith(
         expect.objectContaining({
-          name: 'cg-global-orders',
+          name: 'cg-orderly',
           subscriptionDataType: 'FIFO',
-          deliveryOrderType: 'MESSAGES_ORDER',
+          deliveryOrderType: 'PARTITON_ORDER',
         }),
       ),
     );
diff --git a/web/src/pages/instance/consumer.tsx 
b/web/src/pages/instance/consumer.tsx
index 35925203d..41b426e92 100644
--- a/web/src/pages/instance/consumer.tsx
+++ b/web/src/pages/instance/consumer.tsx
@@ -34,6 +34,7 @@ import {
   Descriptions,
   Statistic,
   Radio,
+  Segmented,
   InputNumber,
   Typography,
   Row,
@@ -122,6 +123,13 @@ const { Text } = Typography;
 const UNKNOWN_LAG_COLOR = '#8c8c8c';
 const UNAVAILABLE_LAG_LABEL = '不可用';
 
+// 消费组只区分普通/顺序:延迟与事务是 Topic 的消息类型;顺序组固定分区顺序(云 OpenAPI 只有 Orderly)
+const GROUP_TYPE_CARDS = [
+  { value: 'NORMAL', label: '普通消息', desc: '并发消费,吞吐优先,适用于绝大多数场景。' },
+  { value: 'FIFO', label: '顺序消息', desc: '按分区顺序消费,需搭配顺序 Topic 使用。' },
+];
+const ORDERED_DELIVERY_TYPE = 'PARTITON_ORDER'; // 拼写与后端 
normalizeDeliveryOrderType、CSV 导入词表一致
+
 /**
  * The provider leaves delaySeconds at zero when the broker consume stats 
carried no
  * consumed-message timestamp (RocketMQMetadataProvider: "keep zeros") and 
says so through
@@ -304,7 +312,7 @@ const ConsumerPageContent = ({
   }>();
   const [createModalOpen, setCreateModalOpen] = useState(false);
   const [form] = Form.useForm();
-  const [dataTypeValue, setDataTypeValue] = useState<string | 
undefined>(undefined);
+  const createDataType = Form.useWatch('dataType', form);
   const [resetModalOpen, setResetModalOpen] = useState(false);
   const [resetGroup, setResetGroup] = useState<ConsumerGroup | null>(null);
   const [resetTopic, setResetTopic] = useState<string>();
@@ -2502,7 +2510,6 @@ const ConsumerPageContent = ({
         onCancel={() => {
           setCreateModalOpen(false);
           form.resetFields();
-          setDataTypeValue(undefined);
         }}
         onOk={() => {
           form
@@ -2525,8 +2532,9 @@ const ConsumerPageContent = ({
                       subscriptionMode: values.subscriptionMode,
                       consumeType: values.consumeType,
                       retryMaxTimes: values.retryMaxTimes,
-                      subscriptionDataType: values.dataType || 'NORMAL',
-                      deliveryOrderType: values.deliveryOrderType,
+                      subscriptionDataType: values.dataType,
+                      deliveryOrderType:
+                        values.dataType === 'FIFO' ? ORDERED_DELIVERY_TYPE : 
undefined,
                       subscribedTopics: [],
                       instanceId: selectedInstanceId,
                     });
@@ -2537,7 +2545,6 @@ const ConsumerPageContent = ({
                     message.success(`消费组 ${values.name} 创建成功`);
                     setCreateModalOpen(false);
                     form.resetFields();
-                    setDataTypeValue(undefined);
                   } catch {
                     message.error(t('consumer.createFailed'));
                     throw new Error(t('consumer.createFailed'));
@@ -2563,6 +2570,7 @@ const ConsumerPageContent = ({
             subscriptionMode: 'Push',
             consumeType: 'CLUSTERING',
             retryMaxTimes: 16,
+            dataType: 'NORMAL',
           }}
         >
           <Form.Item
@@ -2610,35 +2618,13 @@ const ConsumerPageContent = ({
             <InputNumber min={0} max={128} style={{ width: '100%' }} />
           </Form.Item>
 
-          <Form.Item label="订阅组类型" name="dataType">
-            <Select
-              placeholder="选择消息类型"
-              options={[
-                { value: 'NORMAL', label: '普通消息' },
-                { value: 'FIFO', label: '顺序消息' },
-                { value: 'DELAY', label: '延迟消息' },
-                { value: 'TRANSACTION', label: '事务消息' },
-              ]}
-              onChange={(val) => setDataTypeValue(val)}
-            />
+          <Form.Item
+            label="订阅组类型"
+            name="dataType"
+            extra={GROUP_TYPE_CARDS.find((c) => c.value === 
createDataType)?.desc}
+          >
+            <Segmented options={GROUP_TYPE_CARDS.map(({ value, label }) => ({ 
value, label }))} />
           </Form.Item>
-
-          {dataTypeValue === 'FIFO' && (
-            <Form.Item label="顺序类型" name="deliveryOrderType" 
initialValue="PARTITON_ORDER">
-              <Select
-                options={[
-                  {
-                    value: 'PARTITON_ORDER',
-                    label: '分区顺序',
-                  },
-                  {
-                    value: 'MESSAGES_ORDER',
-                    label: '全局顺序',
-                  },
-                ]}
-              />
-            </Form.Item>
-          )}
         </Form>
       </Modal>
 
diff --git a/web/src/pages/ops/AuditRiskInsights.tsx 
b/web/src/pages/ops/AuditRiskInsights.tsx
index c9afa7340..bfca512e7 100644
--- a/web/src/pages/ops/AuditRiskInsights.tsx
+++ b/web/src/pages/ops/AuditRiskInsights.tsx
@@ -235,9 +235,9 @@ const AuditRiskInsights = ({ summary, records, loading }: 
Props) => {
       }
       style={{ marginBottom: 16 }}
     >
-      <Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
+      <Row gutter={[12, 12]} align="stretch" style={{ marginBottom: 16 }}>
         <Col xs={12} lg={12}>
-          <Card size="small">
+          <Card size="small" style={{ height: '100%' }}>
             <Statistic
               title={t('auditInsights.controlPlaneFailures')}
               value={insights.controlPlaneFailureCount}
@@ -251,7 +251,7 @@ const AuditRiskInsights = ({ summary, records, loading }: 
Props) => {
           </Card>
         </Col>
         <Col xs={12} lg={12}>
-          <Card size="small">
+          <Card size="small" style={{ height: '100%' }}>
             <Statistic
               title={t('auditInsights.topOperator')}
               value={insights.topOperator?.name ?? '-'}
diff --git a/web/src/pages/ops/AuditSummaryCards.tsx 
b/web/src/pages/ops/AuditSummaryCards.tsx
index 6299d60a3..7fba554fb 100644
--- a/web/src/pages/ops/AuditSummaryCards.tsx
+++ b/web/src/pages/ops/AuditSummaryCards.tsx
@@ -79,14 +79,14 @@ const AuditSummaryCards = ({ summary, loading }: Props) => {
   // 成功率配色按阈值走,不再写死绿色(用户约定 2026-10-16):47% 曾照样显示绿色,读起来像健康。
   const successColor = successRate >= 95 ? '#52c41a' : successRate >= 80 ? 
'#faad14' : '#cf1322';
   return (
-    <Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
+    <Row gutter={[12, 12]} align="stretch" style={{ marginBottom: 16 }}>
       <Col xs={12} lg={6}>
-        <Card size="small">
+        <Card size="small" style={{ height: '100%' }}>
           <Statistic title={t('audit.summaryMatched')} value={data.total} />
         </Card>
       </Col>
       <Col xs={12} lg={6}>
-        <Card size="small">
+        <Card size="small" style={{ height: '100%' }}>
           <Statistic
             title={t('audit.summarySuccessRate')}
             value={successRate}
@@ -104,7 +104,7 @@ const AuditSummaryCards = ({ summary, loading }: Props) => {
         </Card>
       </Col>
       <Col xs={12} lg={6}>
-        <Card size="small">
+        <Card size="small" style={{ height: '100%' }}>
           <Statistic
             title={t('auditInsights.failPartial')}
             value={`${data.failed} / ${data.partial}`}
@@ -113,17 +113,17 @@ const AuditSummaryCards = ({ summary, loading }: Props) 
=> {
         </Card>
       </Col>
       <Col xs={12} lg={6}>
-        <Card size="small">
+        <Card size="small" style={{ height: '100%' }}>
           <Statistic title={t('audit.summaryOperators')} 
value={data.uniqueOperators} />
         </Card>
       </Col>
       <Col xs={24} lg={12}>
-        <Card size="small" title={t('audit.summaryTopOperations')}>
+        <Card size="small" title={t('audit.summaryTopOperations')} style={{ 
height: '100%' }}>
           <BucketList items={data.byOperation} total={data.total} 
kind="operation" />
         </Card>
       </Col>
       <Col xs={24} lg={12}>
-        <Card size="small" title={t('audit.summaryResourceTypes')}>
+        <Card size="small" title={t('audit.summaryResourceTypes')} style={{ 
height: '100%' }}>
           <BucketList items={data.byResourceType} total={data.total} 
kind="resource" />
         </Card>
       </Col>

Reply via email to