This is an automated email from the ASF dual-hosted git repository.

dockerzhang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/inlong.git


The following commit(s) were added to refs/heads/master by this push:
     new da568ab768 [INLONG-11894][Dashboard] Added group and stream switching 
on the audit page (#11895)
da568ab768 is described below

commit da568ab768be173d7c52d9a04cbe799affb66fd9
Author: kamianlaida <[email protected]>
AuthorDate: Tue Jun 17 18:02:44 2025 +0800

    [INLONG-11894][Dashboard] Added group and stream switching on the audit 
page (#11895)
---
 inlong-dashboard/src/ui/locales/cn.json            |   5 +
 inlong-dashboard/src/ui/locales/en.json            |   5 +
 .../src/ui/pages/GroupDetail/Audit/config.tsx      | 437 ++++++++++++---------
 .../src/ui/pages/GroupDetail/Audit/index.less      |  34 ++
 .../src/ui/pages/GroupDetail/Audit/index.tsx       | 153 +++++---
 5 files changed, 402 insertions(+), 232 deletions(-)

diff --git a/inlong-dashboard/src/ui/locales/cn.json 
b/inlong-dashboard/src/ui/locales/cn.json
index 26dac255ae..fdcf098fb1 100644
--- a/inlong-dashboard/src/ui/locales/cn.json
+++ b/inlong-dashboard/src/ui/locales/cn.json
@@ -706,6 +706,11 @@
   "pages.GroupDetail.Streams": "数据流",
   "pages.GroupDetail.Sources": "数据源",
   "pages.GroupDetail.Sinks": "数据目标",
+  "pages.GroupDetail.SinkType": "数据目标类型",
+  "pages.GroupDetail.Audit.Count": "数量",
+  "pages.GroupDetail.Audit.Group": "数据流组",
+  "pages.GroupDetail.Audit.Stream": "数据流",
+  "pages.GroupDetail.Audit.Size": "大小",
   "pages.GroupDetail.Audit": "审计对账",
   "pages.GroupDetail.Resource": "资源详情",
   "pages.GroupDetail.Delay": "传输时延",
diff --git a/inlong-dashboard/src/ui/locales/en.json 
b/inlong-dashboard/src/ui/locales/en.json
index f6244380d3..6e479235de 100644
--- a/inlong-dashboard/src/ui/locales/en.json
+++ b/inlong-dashboard/src/ui/locales/en.json
@@ -701,6 +701,11 @@
   "pages.GroupDetail.Streams": "Streams",
   "pages.GroupDetail.Sources": "Sources",
   "pages.GroupDetail.Sinks": "Sinks",
+  "pages.GroupDetail.SinkType": "Sink Type",
+  "pages.GroupDetail.Audit.Count": "Count",
+  "pages.GroupDetail.Audit.Group": "Group",
+  "pages.GroupDetail.Audit.Stream": "Stream",
+  "pages.GroupDetail.Audit.Size": "Size",
   "pages.GroupDetail.Audit": "Audit",
   "pages.GroupDetail.Delay": "Transmission delay",
   "pages.GroupDetail.OperationLog": "Operation log",
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
index c9bd6f6852..1eef8494e2 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/config.tsx
@@ -119,24 +119,28 @@ export const getSourceDataWithPercent = (sourceKeys, 
sourceMap) => {
     const keys = Object.keys(source).filter(key => key !== 'logTs');
     const firstKey = keys[0];
     const firstValue = source[firstKey];
+    const dpValue = source[keys[2]] || 0;
     newSource[firstKey] = firstValue.toString();
     for (let key of keys.slice(1)) {
-      if (key !== 'logTs') {
-        let diff = getDiff(firstValue, source[key]);
-        newSource[key] = `${source[key]} (${diff})`;
+      let diff = '0%';
+      if (firstValue === 0 && dpValue === 0) {
+        diff = firstValue.toFixed(4) + '%';
+      }
+      if ((key === '4' || key === '5') && firstValue === 0) {
+        diff = firstValue.toFixed(4) + '%';
+      } else {
+        diff = getDiff(firstValue, dpValue, source[key]);
       }
+      newSource[key] = `${source[key]} (${diff})`;
     }
     newSource['logTs'] = source['logTs'];
     return newSource;
   });
 };
 
-export const getDiff = (first, current) => {
-  if (first === 0) {
-    return first.toFixed(4) + '%';
-  }
+export const getDiff = (first, dpValue, current) => {
   let result;
-  const diff = (current / first - 1) * 100;
+  const diff = (current / (first === 0 ? dpValue : first) - 1) * 100;
   result = diff > 0 ? '+' + diff.toFixed(4) + '%' : diff.toFixed(4) + '%';
   return result;
 };
@@ -157,6 +161,132 @@ export const getSourceDataWithCommas = sourceData => {
   return sourceData;
 };
 let endTimeVisible = true;
+
+const getCommonFormContent = (initialValues, onSearch, sourceData, csvData, 
fileName): any[] => {
+  return [
+    {
+      type: 'datepicker',
+      label: i18n.t('pages.GroupDetail.Audit.StartDate'),
+      name: 'startDate',
+      initialValue: dayjs(initialValues.startDate),
+      props: {
+        allowClear: false,
+        format: 'YYYY-MM-DD',
+      },
+    },
+    {
+      type: 'datepicker',
+      label: i18n.t('pages.GroupDetail.Audit.EndDate'),
+      name: 'endDate',
+      initialValue: dayjs(initialValues.endDate),
+      rules: [
+        { required: true },
+        ({ getFieldValue }) => ({
+          validator(_, value) {
+            const dim = initialValues.timeStaticsDim;
+            if (dim === 'MINUTE') {
+              return Promise.resolve();
+            }
+            const timeDiff = value - getFieldValue('startDate');
+            console.log(timeDiff);
+            if (timeDiff >= 0) {
+              const isHourDiff = dim === 'HOUR' && timeDiff < 1000 * 60 * 60 * 
24 * 2;
+              const isDayDiff = dim === 'DAY' && timeDiff < 1000 * 60 * 60 * 
24 * 90;
+              if (isHourDiff || isDayDiff) {
+                return Promise.resolve();
+              }
+            }
+            return Promise.reject(new 
Error(i18n.t('pages.GroupDetail.Audit.DatepickerRule')));
+          },
+        }),
+      ],
+      props: {
+        allowClear: false,
+        format: 'YYYY-MM-DD',
+        disabled: initialValues.timeStaticsDim === 'MINUTE',
+        disabledDate: current => {
+          const start = dayjs(initialValues.startDate);
+          const dim = initialValues.timeStaticsDim;
+          const tooEarly = current < start.add(-1, 'd').endOf('day');
+          let tooLate;
+          if (dim === 'HOUR') {
+            tooLate = current >= start.add(2, 'd').endOf('day');
+          }
+          if (dim === 'DAY') {
+            tooLate = current >= start.add(90, 'd').endOf('day');
+          }
+          return current && (tooLate || tooEarly);
+        },
+      },
+    },
+    {
+      type: 'select',
+      label: i18n.t('pages.GroupDetail.Audit.TimeStaticsDim'),
+      name: 'timeStaticsDim',
+      initialValue: initialValues.timeStaticsDim,
+      props: {
+        dropdownMatchSelectWidth: false,
+        options: timeStaticsDimList,
+      },
+    },
+    {
+      type: 'select',
+      label: i18n.t('pages.GroupDetail.Audit.Item'),
+      name: 'auditIds',
+      props: {
+        style: {
+          width: 200,
+        },
+        mode: 'multiple',
+        allowClear: true,
+        showSearch: true,
+        dropdownMatchSelectWidth: false,
+        options: {
+          requestAuto: true,
+          requestTrigger: ['onOpen'],
+          requestService: () => {
+            return request('/audit/getAuditBases');
+          },
+          requestParams: {
+            formatResult: result => {
+              return result?.reduce((accumulator, item) => {
+                const existingItem = accumulator.find(
+                  (i: { value: any }) => i.value === item.auditId,
+                );
+                if (!existingItem) {
+                  accumulator.push({
+                    label: i18n?.language === 'cn' ? item.nameInChinese : 
item.nameInEnglish,
+                    value: item.auditId,
+                  });
+                }
+                return accumulator;
+              }, []);
+            },
+          },
+        },
+        filterOption: (keyword: string, option: { label: any }) => {
+          return (option?.label ?? 
'').toLowerCase().includes(keyword.toLowerCase());
+        },
+      },
+    },
+    {
+      type: (
+        <Button type="primary" onClick={onSearch}>
+          {i18n.t('pages.GroupDetail.Audit.Search')}
+        </Button>
+      ),
+    },
+    {
+      type: (
+        <Button type="primary" disabled={!(sourceData.length > 0)}>
+          <CSVLink data={csvData} filename={fileName}>
+            {i18n.t('pages.GroupDetail.Audit.ExportCSV')}
+          </CSVLink>
+        </Button>
+      ),
+    },
+  ];
+};
 export const getFormContent = (
   inlongGroupId,
   initialValues,
@@ -167,196 +297,127 @@ export const getFormContent = (
   fileName,
   setInlongStreamID,
   inlongStreamId,
-) => [
-  {
-    type: 'select',
-    label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
-    name: 'inlongStreamId',
-    props: {
-      dropdownMatchSelectWidth: false,
-      showSearch: true,
-      onChange: (value, option) => {
-        setInlongStreamID(value);
-      },
-      options: {
-        requestAuto: true,
-        requestTrigger: ['onOpen', 'onSearch'],
-        requestService: keyword => ({
-          url: '/stream/list',
-          method: 'POST',
-          data: {
-            keyword,
-            pageNum: 1,
-            pageSize: 100,
-            inlongGroupId,
+  key,
+) => {
+  const commonFormContent = getCommonFormContent(
+    initialValues,
+    onSearch,
+    sourceData,
+    csvData,
+    fileName,
+  );
+  if (key === 'stream') {
+    return [
+      {
+        type: 'select',
+        label: i18n.t('pages.ModuleAudit.config.InlongStreamId'),
+        name: 'inlongStreamId',
+        initialValue: inlongStreamId,
+        props: {
+          dropdownMatchSelectWidth: false,
+          showSearch: true,
+          onChange: (value, option) => {
+            setInlongStreamID(value);
+          },
+          options: {
+            requestAuto: true,
+            requestTrigger: ['onOpen', 'onSearch'],
+            requestService: keyword => ({
+              url: '/stream/list',
+              method: 'POST',
+              data: {
+                keyword,
+                pageNum: 1,
+                pageSize: 100,
+                inlongGroupId,
+              },
+            }),
+            requestParams: {
+              formatResult: result =>
+                result?.list.map(item => ({
+                  label: item.inlongStreamId,
+                  value: item.inlongStreamId,
+                })) || [],
+              onSuccess: onDataStreamSuccess,
+            },
           },
-        }),
-        requestParams: {
-          formatResult: result =>
-            result?.list.map(item => ({
-              label: item.inlongStreamId,
-              value: item.inlongStreamId,
-            })) || [],
-          onSuccess: onDataStreamSuccess,
         },
+        rules: [{ required: true }],
       },
-    },
-    rules: [{ required: true }],
-  },
-  {
-    type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.Sink'),
-    name: 'sinkId',
-    props: values => ({
-      dropdownMatchSelectWidth: false,
-      showSearch: true,
-      options: {
-        requestTrigger: ['onOpen', 'onSearch'],
-        requestService: keyword => ({
-          url: '/sink/list',
-          method: 'POST',
-          data: {
-            keyword,
-            pageNum: 1,
-            pageSize: 100,
-            inlongGroupId,
-            inlongStreamId: inlongStreamId,
+      {
+        type: 'select',
+        label: i18n.t('pages.GroupDetail.Audit.Sink'),
+        name: 'sinkId',
+        initialValue: initialValues.sinkId,
+        props: values => ({
+          dropdownMatchSelectWidth: false,
+          showSearch: true,
+          options: {
+            requestTrigger: ['onOpen', 'onSearch'],
+            requestService: keyword => ({
+              url: '/sink/list',
+              method: 'POST',
+              data: {
+                keyword,
+                pageNum: 1,
+                pageSize: 100,
+                inlongGroupId,
+                inlongStreamId: inlongStreamId,
+              },
+            }),
+            requestParams: {
+              formatResult: result =>
+                result?.list.map(item => ({
+                  label:
+                    item.sinkName + ` ( ${sinks.find(c => c.value === 
item.sinkType)?.label} )`,
+                  value: item.id,
+                })) || [],
+            },
+          },
+          filterOption: (keyword: string, option: { label: any }) => {
+            return (option?.label ?? 
'').toLowerCase().includes(keyword.toLowerCase());
           },
         }),
-        requestParams: {
-          formatResult: result =>
-            result?.list.map(item => ({
-              label: item.sinkName + ` ( ${sinks.find(c => c.value === 
item.sinkType)?.label} )`,
-              value: item.id,
-            })) || [],
-        },
-      },
-      filterOption: (keyword: string, option: { label: any }) => {
-        return (option?.label ?? 
'').toLowerCase().includes(keyword.toLowerCase());
-      },
-    }),
-  },
-  {
-    type: 'datepicker',
-    label: i18n.t('pages.GroupDetail.Audit.StartDate'),
-    name: 'startDate',
-    initialValue: dayjs(initialValues.startDate),
-    props: {
-      allowClear: false,
-      format: 'YYYY-MM-DD',
-    },
-  },
-  {
-    type: 'datepicker',
-    label: i18n.t('pages.GroupDetail.Audit.EndDate'),
-    name: 'endDate',
-    initialValue: dayjs(initialValues.endDate),
-    rules: [
-      { required: true },
-      ({ getFieldValue }) => ({
-        validator(_, value) {
-          const dim = initialValues.timeStaticsDim;
-          if (dim === 'MINUTE') {
-            return Promise.resolve();
-          }
-          const timeDiff = value - getFieldValue('startDate');
-          if (timeDiff >= 0) {
-            const isHourDiff = dim === 'HOUR' && timeDiff < 1000 * 60 * 60 * 
24 * 3;
-            const isDayDiff = dim === 'DAY' && timeDiff < 1000 * 60 * 60 * 24 
* 7;
-            if (isHourDiff || isDayDiff) {
-              return Promise.resolve();
-            }
-          }
-          return Promise.reject(new 
Error(i18n.t('pages.GroupDetail.Audit.DatepickerRule')));
-        },
-      }),
-    ],
-    props: {
-      allowClear: false,
-      format: 'YYYY-MM-DD',
-      disabled: initialValues.timeStaticsDim === 'MINUTE',
-      disabledDate: current => {
-        const start = dayjs(initialValues.startDate);
-        const dim = initialValues.timeStaticsDim;
-        const tooEarly = current < start.add(-1, 'd').endOf('day');
-        let tooLate;
-        if (dim === 'HOUR') {
-          tooLate = current >= start.add(2, 'd').endOf('day');
-        }
-        if (dim === 'DAY') {
-          tooLate = current >= start.add(6, 'd').endOf('day');
-        }
-        return current && (tooLate || tooEarly);
       },
-    },
-  },
-  {
-    type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.TimeStaticsDim'),
-    name: 'timeStaticsDim',
-    initialValue: initialValues.timeStaticsDim,
-    props: {
-      dropdownMatchSelectWidth: false,
-      options: timeStaticsDimList,
-    },
-  },
-  {
-    type: 'select',
-    label: i18n.t('pages.GroupDetail.Audit.Item'),
-    name: 'auditIds',
-    props: {
-      style: {
-        width: 200,
-      },
-      mode: 'multiple',
-      allowClear: true,
-      showSearch: true,
-      dropdownMatchSelectWidth: false,
-      options: {
-        requestAuto: true,
-        requestTrigger: ['onOpen'],
-        requestService: () => {
-          return request('/audit/getAuditBases');
-        },
-        requestParams: {
-          formatResult: result => {
-            return result?.reduce((accumulator, item) => {
-              const existingItem = accumulator.find(
-                (i: { value: any }) => i.value === item.auditId,
-              );
-              if (!existingItem) {
-                accumulator.push({
-                  label: i18n?.language === 'cn' ? item.nameInChinese : 
item.nameInEnglish,
-                  value: item.auditId,
-                });
-              }
-              return accumulator;
-            }, []);
+    ].concat(commonFormContent);
+  } else {
+    const sinkTypeList = sinks
+      .filter(item => item.value !== '')
+      .map(item => {
+        return {
+          label: item.label,
+          value: item.value,
+        };
+      });
+    return [
+      {
+        type: 'input',
+        label: i18n.t('meta.Group.InlongGroupId'),
+        name: 'inlongGroupId',
+        initialValue: inlongGroupId,
+        props: {
+          disabled: true,
+          style: {
+            width: 200,
           },
         },
+        rules: [{ required: true }],
       },
-      filterOption: (keyword: string, option: { label: any }) => {
-        return (option?.label ?? 
'').toLowerCase().includes(keyword.toLowerCase());
+      {
+        type: 'select',
+        label: i18n.t('pages.GroupDetail.SinkType'),
+        name: 'sinkType',
+        initialValue: initialValues.sinkType,
+        props: {
+          allowClear: true,
+          dropdownMatchSelectWidth: false,
+          showSearch: true,
+          options: sinkTypeList,
+        },
       },
-    },
-  },
-  {
-    type: (
-      <Button type="primary" onClick={onSearch}>
-        {i18n.t('pages.GroupDetail.Audit.Search')}
-      </Button>
-    ),
-  },
-  {
-    type: (
-      <Button type="primary" disabled={!(sourceData.length > 0)}>
-        <CSVLink data={csvData} filename={fileName}>
-          {i18n.t('pages.GroupDetail.Audit.ExportCSV')}
-        </CSVLink>
-      </Button>
-    ),
-  },
-];
+    ].concat(commonFormContent);
+  }
+};
 
 export const getTableColumns = (source, dim) => {
   const data = source.map(item => ({
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.less 
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.less
new file mode 100644
index 0000000000..dd7b2ff716
--- /dev/null
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.less
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+.audit-container{
+  position: relative;
+
+  .chart-type{
+    position: absolute;
+    top: 10px;
+    right: 0px;
+    height: 30px;
+  }
+
+  .chart-container{
+    padding-top: 50px;
+  }
+}
diff --git a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx 
b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
index 913d583eb4..9b94eb2ff6 100644
--- a/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
+++ b/inlong-dashboard/src/ui/pages/GroupDetail/Audit/index.tsx
@@ -31,20 +31,24 @@ import {
   getTableColumns,
   timeStaticsDimList,
 } from './config';
-import { Table } from 'antd';
+import { Table, Radio, Tabs, TabsProps } from 'antd';
 import i18n from '@/i18n';
-
+import './index.less';
 type Props = CommonInterface;
-
+const initialQuery = {
+  inlongStreamId: null,
+  startDate: +new Date(),
+  endDate: +new Date(),
+  timeStaticsDim: timeStaticsDimList[0].value,
+  sinkId: null,
+  sinkType: null,
+};
 const Comp: React.FC<Props> = ({ inlongGroupId }) => {
   const [form] = useForm();
-  const [query, setQuery] = useState({
-    inlongStreamId: '',
-    startDate: +new Date(),
-    endDate: +new Date(),
-    timeStaticsDim: timeStaticsDimList[0].value,
-  });
+  const [query, setQuery] = useState(initialQuery);
   const [inlongStreamID, setInlongStreamID] = useState('');
+  const [type, setType] = useState('count');
+  const [subTab, setSubTab] = useState('stream');
   const { data: sourceData = [], run } = useRequest(
     {
       url: '/audit/list',
@@ -57,7 +61,7 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
       },
     },
     {
-      ready: Boolean(query.inlongStreamId),
+      refreshDeps: [query],
       formatResult: result => result.sort((a, b) => (a.auditId - b.auditId > 0 
? 1 : -1)),
     },
   );
@@ -79,23 +83,39 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
         const bT = +new Date(query.timeStaticsDim === 'HOUR' ? `${b.logTs}:00` 
: b.logTs);
         return aT - bT;
       });
-    const output = flatArr.reduce((acc, cur) => {
-      if (!acc[cur.logTs]) {
-        acc[cur.logTs] = {};
-      }
-      acc[cur.logTs] = {
-        ...acc[cur.logTs],
-        [cur.auditId]: cur.count,
-      };
-      return acc;
-    }, {});
+    let output: any;
+    if (type === 'count') {
+      output = flatArr.reduce((acc, cur) => {
+        if (!acc[cur.logTs]) {
+          acc[cur.logTs] = {};
+        }
+        acc[cur.logTs] = {
+          ...acc[cur.logTs],
+          [cur.auditId]: cur.count,
+        };
+        return acc;
+      }, {});
+    } else {
+      output = flatArr.reduce((acc, cur) => {
+        if (!acc[cur.logTs]) {
+          acc[cur.logTs] = {};
+        }
+        acc[cur.logTs] = {
+          ...acc[cur.logTs],
+          [cur.auditId]: cur.size,
+        };
+        return acc;
+      }, {});
+    }
     return output;
-  }, [sourceData, query.timeStaticsDim]);
+  }, [sourceData, query.timeStaticsDim, type]);
 
   const onSearch = async () => {
     let values = await form.validateFields();
     if (values.timeStaticsDim == 'MINUTE') {
       setQuery(prev => ({ ...prev, endDate: prev.startDate }));
+    } else {
+      setQuery(values);
     }
     run();
   };
@@ -109,6 +129,7 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
       run();
     }
   };
+
   const numToName = useCallback(
     num => {
       let obj = {};
@@ -146,9 +167,32 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
   const [fileName, setFileName] = useState('audit.csv');
   useEffect(() => {
     setFileName(`audit_${inlongGroupId}_${inlongStreamID}.csv`);
+    form.setFieldsValue({ sinkId: '' });
   }, [inlongGroupId, inlongStreamID]);
+
+  const onChange = e => {
+    setSubTab(e.target.value);
+    setInlongStreamID(undefined);
+    const value = form.getFieldsValue();
+    console.log('form value', value);
+    const tmp = { ...query };
+    if (e.target.value === 'group') {
+      delete tmp.inlongStreamId;
+      delete tmp.sinkId;
+    } else {
+      delete tmp.sinkType;
+    }
+    setQuery(tmp);
+  };
+
   return (
     <>
+      <div style={{ marginBottom: 20 }}>
+        <Radio.Group defaultValue={subTab} buttonStyle="solid" onChange={e => 
onChange(e)}>
+          <Radio.Button 
value="group">{i18n.t('pages.GroupDetail.Audit.Group')}</Radio.Button>
+          <Radio.Button 
value="stream">{i18n.t('pages.GroupDetail.Audit.Stream')}</Radio.Button>
+        </Radio.Group>
+      </div>
       <div style={{ marginBottom: 40 }}>
         <FormGenerator
           form={form}
@@ -163,6 +207,7 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
             fileName,
             setInlongStreamID,
             inlongStreamID,
+            subTab,
           )}
           style={{ marginBottom: 30, gap: 10 }}
           onFilter={allValues =>
@@ -173,30 +218,50 @@ const Comp: React.FC<Props> = ({ inlongGroupId }) => {
             })
           }
         />
-        <Charts height={400} option={toChartData(sourceData, sourceDataMap)} 
forceUpdate={true} />
       </div>
-
-      <HighTable
-        table={{
-          columns: getTableColumns(sourceData, query.timeStaticsDim),
-          dataSource: toTableData(sourceData, sourceDataMap),
-          rowKey: 'logTs',
-          summary: () => (
-            <Table.Summary fixed>
-              <Table.Summary.Row>
-                <Table.Summary.Cell index={0}>
-                  {i18n.t('pages.GroupDetail.Audit.Total')}
-                </Table.Summary.Cell>
-                {sourceData.map((row, index) => (
-                  <Table.Summary.Cell index={index + 1}>
-                    {row.auditSet.reduce((total, item) => total + item.count, 
0).toLocaleString()}
-                  </Table.Summary.Cell>
-                ))}
-              </Table.Summary.Row>
-            </Table.Summary>
-          ),
-        }}
-      />
+      <div className="audit-container">
+        <div className="chart-type">
+          <Radio.Group
+            defaultValue="count"
+            buttonStyle="solid"
+            onChange={e => setType(e.target.value)}
+          >
+            <Radio.Button 
value="size">{i18n.t('pages.GroupDetail.Audit.Size')}</Radio.Button>
+            <Radio.Button 
value="count">{i18n.t('pages.GroupDetail.Audit.Count')}</Radio.Button>
+          </Radio.Group>
+        </div>
+        <div className="chart-container">
+          <Charts height={400} option={toChartData(sourceData, sourceDataMap)} 
forceUpdate={true} />
+        </div>
+        <div className="table-container">
+          <HighTable
+            table={{
+              columns: getTableColumns(sourceData, query.timeStaticsDim),
+              dataSource: toTableData(sourceData, sourceDataMap),
+              rowKey: 'logTs',
+              pagination: {
+                pageSizeOptions: ['10', '20', '50', '60', '100', '120'],
+              },
+              summary: () => (
+                <Table.Summary fixed>
+                  <Table.Summary.Row>
+                    <Table.Summary.Cell index={0}>
+                      {i18n.t('pages.GroupDetail.Audit.Total')}
+                    </Table.Summary.Cell>
+                    {sourceData.map((row, index) => (
+                      <Table.Summary.Cell index={index + 1}>
+                        {row.auditSet
+                          .reduce((total, item) => total + item.count, 0)
+                          .toLocaleString()}
+                      </Table.Summary.Cell>
+                    ))}
+                  </Table.Summary.Row>
+                </Table.Summary>
+              ),
+            }}
+          />
+        </div>
+      </div>
     </>
   );
 };

Reply via email to