You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@inlong.apache.org by do...@apache.org on 2022/09/07 11:00:03 UTC

[inlong] branch master updated: [INLONG-5798][Dashboard] Support stream fields extends (#5811)

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 76f5d18fb [INLONG-5798][Dashboard] Support stream fields extends (#5811)
76f5d18fb is described below

commit 76f5d18fb62722e0bdcf1f412f9fa1dafd4fc0cf
Author: Daniel <le...@apache.org>
AuthorDate: Wed Sep 7 18:59:58 2022 +0800

    [INLONG-5798][Dashboard] Support stream fields extends (#5811)
---
 inlong-dashboard/src/locales/cn.json               |  51 ++-
 inlong-dashboard/src/locales/en.json               |  53 ++-
 .../src/metas/clusters/{Agent.tsx => agent.tsx}    |   4 +-
 .../clusters/{DataProxy.tsx => dataProxy.tsx}      |   4 +-
 inlong-dashboard/src/metas/clusters/index.tsx      |  60 ++--
 .../src/metas/clusters/{Pulsar.tsx => pulsar.tsx}  |   4 +-
 .../src/metas/clusters/{TubeMQ.tsx => tubeMQ.tsx}  |   4 +-
 inlong-dashboard/src/metas/sinks/hive.tsx          |  12 +-
 inlong-dashboard/src/metas/sources/binLog.ts       |  18 -
 .../common/types.ts => stream/extends/index.tsx}   |  11 +-
 inlong-dashboard/src/metas/stream/index.tsx        | 363 +++++++++++----------
 .../DataStream => metas/stream}/status.tsx         |   8 +-
 .../src/pages/ClusterTags/ClusterList.tsx          |  20 +-
 .../src/pages/Clusters/CreateModal.tsx             |  10 +-
 inlong-dashboard/src/pages/Clusters/index.tsx      |  81 +++--
 .../GroupDetail/DataStream/StreamItemModal.tsx     |  50 +--
 .../src/pages/GroupDetail/DataStream/config.tsx    |  18 +-
 .../src/pages/GroupDetail/DataStream/helper.ts     |  73 ++---
 .../src/pages/GroupDetail/DataStream/index.tsx     |  31 +-
 19 files changed, 383 insertions(+), 492 deletions(-)

diff --git a/inlong-dashboard/src/locales/cn.json b/inlong-dashboard/src/locales/cn.json
index 948e13cd5..9f2c63506 100644
--- a/inlong-dashboard/src/locales/cn.json
+++ b/inlong-dashboard/src/locales/cn.json
@@ -34,7 +34,6 @@
   "meta.Sources.Db.DatabaseWhiteList": "库名白名单",
   "meta.Sources.Db.TableWhiteList": "表名白名单",
   "meta.Sources.Db.WhiteListHelp": "多个白名单之间用英文逗号分隔,单个为正则表达式,比如:b1,b*",
-  "components.AccessHelper.DataStorageEditor.Editor.AddTo": "添加",
   "meta.Sinks.SinkName": "名称",
   "meta.Sinks.SinkNameRule": "以英文字母开头,只能包含英文字母、数字、中划线、下划线",
   "meta.Sinks.SinkType": "类型",
@@ -50,6 +49,12 @@
   "meta.Sinks.Hive.Day": "天",
   "meta.Sinks.Hive.DataEncoding": "数据编码",
   "meta.Sinks.Hive.DataSeparator": "字段分隔符",
+  "meta.Sinks.Hive.DataSeparator.Space": "空格",
+  "meta.Sinks.Hive.DataSeparator.Asterisk": "星号(*)",
+  "meta.Sinks.Hive.DataSeparator.Comma": "逗号(,)",
+  "meta.Sinks.Hive.DataSeparator.Semicolon": "分号(;)",
+  "meta.Sinks.Hive.DataSeparator.VerticalLine": "竖线(|)",
+  "meta.Sinks.Hive.DataSeparator.DoubleQuotes": "双引号(\")",
   "meta.Sinks.Hive.PartitionFieldList": "分区字段",
   "meta.Sinks.Hive.PartitionFieldListHelp": "字段类型若为timestamp,则必须设置此字段值的格式,支持 MICROSECONDS,MILLISECONDS,SECONDS,SQL,ISO_8601,以及自定义,比如:yyyy-MM-dd HH:mm:ss 等",
   "meta.Sinks.Hive.DbName": "DB名称",
@@ -226,32 +231,29 @@
   "meta.Group.Status.BeSubmitted": "待提交",
   "meta.Group.Status.BeApproved": "待审批",
   "meta.Group.Status.ConfigurationFailed": "配置失败",
-  "meta.Stream.DataStreamID": "数据流ID",
-  "meta.Stream.InlongStreamIdRules": "只能包含小写字母、数字、中划线、下划线",
-  "meta.Stream.fileDelimiter": "源数据字段分割符",
-  "meta.Stream.Asterisk": "星号(*)",
-  "meta.Stream.DataStreamName": "数据流名称",
+  "meta.Stream.StreamId": "数据流ID",
+  "meta.Stream.StreamIdRules": "只能包含小写字母、数字、中划线、下划线",
+  "meta.Stream.DataSeparator": "源数据字段分割符",
+  "meta.Stream.DataSeparator.Space": "空格",
+  "meta.Stream.DataSeparator.Asterisk": "星号(*)",
+  "meta.Stream.DataSeparator.Comma": "逗号(,)",
+  "meta.Stream.DataSeparator.Semicolon": "分号(;)",
+  "meta.Stream.DataSeparator.VerticalLine": "竖线(|)",
+  "meta.Stream.DataSeparator.DoubleQuotes": "双引号(\")",
+  "meta.Stream.StreamName": "数据流名称",
   "meta.Stream.FieldName": "字段名",
   "meta.Stream.FieldNameRule": "以英文字母开头,只能包含英文字母、数字、下划线",
-  "meta.Stream.Semicolon": "分号(;)",
-  "meta.Stream.AutoConsumption": "自主消费",
-  "meta.Stream.DataFlowDirection": "数据流向",
   "meta.Stream.DataType": "数据格式",
   "meta.Stream.DataTypeCsvHelp": "CSV:按特定分隔符分隔的任意文件",
-  "meta.Stream.Source": "消息来源",
   "meta.Stream.FieldType": "字段类型",
   "meta.Stream.FieldComment": "字段描述",
-  "meta.Stream.DoubleQuotes": "双引号(\")",
   "meta.Stream.DataEncoding": "数据编码",
-  "meta.Stream.DataStreamOwners": "责任人",
-  "meta.Stream.DataStreamOwnerHelp": "责任人可查看、修改数据流信息",
-  "meta.Stream.Space": "空格",
-  "meta.Stream.Comma": "逗号(,)",
-  "meta.Stream.DataFlowIntroduction": "介绍",
+  "meta.Stream.Description": "介绍",
   "meta.Stream.SourceDataField": "源数据字段",
-  "meta.Stream.VerticalLine": "竖线(|)",
-  "meta.Stream.File": "文件",
-  "meta.Stream.Autonomous": "自主推送",
+  "meta.Stream.Status.New": "新建",
+  "meta.Stream.Status.Pending": "配置中",
+  "meta.Stream.Status.Error": "配置失败",
+  "meta.Stream.Status.Success": "配置成功",
   "meta.Consumption.Owner": "消费责任人",
   "meta.Consumption.ConsumerGroupName": "消费组名称",
   "meta.Consumption.ConsumerGroupNameRules": "只能包含小写字母、数字、中划线、下划线",
@@ -339,17 +341,8 @@
   "pages.GroupDetail.Sink.Status.Pending": "配置中",
   "pages.GroupDetail.Sink.Status.Error": "配置失败",
   "pages.GroupDetail.Sink.Status.Success": "配置成功",
-  "pages.GroupDetail.Stream.config.Scale": "接入规模",
-  "pages.GroupDetail.Stream.config.Basic": "基础信息",
-  "pages.GroupDetail.Stream.config.DataInfo": "数据信息",
-  "pages.GroupDetail.Stream.config.DataStorages": "数据流向",
-  "pages.GroupDetail.Stream.StreamItemModal.DataFlowConfiguration": "数据流配置",
-  "pages.GroupDetail.Stream.Name": "数据流",
+  "pages.GroupDetail.Stream.StreamConfigTitle": "数据流配置",
   "pages.GroupDetail.Stream.CreateDataStream": "新建数据流",
-  "pages.GroupDetail.Stream.Status.New": "新建",
-  "pages.GroupDetail.Stream.Status.Pending": "配置中",
-  "pages.GroupDetail.Stream.Status.Error": "配置失败",
-  "pages.GroupDetail.Stream.Status.Success": "配置成功",
   "pages.GroupDetail.PageTitle": "详情",
   "pages.GroupDetail.Info": "数据分组",
   "pages.GroupDetail.Streams": "数据流",
diff --git a/inlong-dashboard/src/locales/en.json b/inlong-dashboard/src/locales/en.json
index 266b5a3ee..04b80d59d 100644
--- a/inlong-dashboard/src/locales/en.json
+++ b/inlong-dashboard/src/locales/en.json
@@ -34,7 +34,6 @@
   "meta.Sources.Db.DatabaseWhiteList": "Database WhiteList",
   "meta.Sources.Db.TableWhiteList": "Table WhiteList",
   "meta.Sources.Db.WhiteListHelp": "Multiple whitelists are separated by commas, each of which is a regular expression, for example b1,b*",
-  "components.AccessHelper.DataStorageEditor.Editor.AddTo": "Add",
   "meta.Sinks.SinkName": "Name",
   "meta.Sinks.SinkNameRule": "At the beginning of English letters, only English letters, numbers, minus, and underscores",
   "meta.Sinks.SinkType": "Type",
@@ -50,6 +49,12 @@
   "meta.Sinks.Hive.Day": "Day(s)",
   "meta.Sinks.Hive.DataEncoding": "Data encoding",
   "meta.Sinks.Hive.DataSeparator": "Data separator",
+  "meta.Sinks.Hive.DataSeparator.Space": "Space",
+  "meta.Sinks.Hive.DataSeparator.Asterisk": "Asterisk(*)",
+  "meta.Sinks.Hive.DataSeparator.Comma": "Comma(,)",
+  "meta.Sinks.Hive.DataSeparator.Semicolon": "Semicolon(;)",
+  "meta.Sinks.Hive.DataSeparator.VerticalLine": "Vertical line (|)",
+  "meta.Sinks.Hive.DataSeparator.DoubleQuotes": "Double quotes(\")",
   "meta.Sinks.Hive.PartitionFieldList": "PartitionFieldList",
   "meta.Sinks.Hive.PartitionFieldListHelp": "If the field type is timestamp, you must set the format of the field value, support MICROSECONDS, MILLISECONDS, SECONDS, SQL, ISO_8601, and custom, such as: yyyy-MM-dd HH:mm:ss, etc.",
   "meta.Sinks.Hive.DbName": "DbName",
@@ -226,32 +231,29 @@
   "meta.Group.Status.BeSubmitted": "Be submitted",
   "meta.Group.Status.BeApproved": "BeApproved",
   "meta.Group.Status.ConfigurationFailed": "Configuration failed",
-  "meta.Stream.DataStreamID": "Inlong stream ID",
-  "meta.Stream.InlongStreamIdRules": "Only lowercase letters, numbers, minus, and underscores",
-  "meta.Stream.fileDelimiter": "Source data fileDelimiter",
-  "meta.Stream.Asterisk": "Asterisk(*)",
-  "meta.Stream.DataStreamName": "Stream name",
+  "meta.Stream.StreamId": "Stream ID",
+  "meta.Stream.StreamIdRules": "Only lowercase letters, numbers, minus, and underscores",
+  "meta.Stream.DataSeparator": "Source data fileDelimiter",
+  "meta.Stream.DataSeparator.Space": "Space",
+  "meta.Stream.DataSeparator.Asterisk": "Asterisk(*)",
+  "meta.Stream.DataSeparator.Comma": "Comma(,)",
+  "meta.Stream.DataSeparator.Semicolon": "Semicolon(;)",
+  "meta.Stream.DataSeparator.VerticalLine": "Vertical line (|)",
+  "meta.Stream.DataSeparator.DoubleQuotes": "Double quotes(\")",
+  "meta.Stream.StreamName": "Stream name",
   "meta.Stream.FieldName": "Field name",
   "meta.Stream.FieldNameRule": "At the beginning of English letters, only English letters, numbers, and underscores",
-  "meta.Stream.Semicolon": "Semicolon(;)",
-  "meta.Stream.AutoConsumption": "Auto Consumption",
-  "meta.Stream.DataFlowDirection": "Stream direction",
   "meta.Stream.DataType": "DataType",
   "meta.Stream.DataTypeCsvHelp": "CSV: Any file separated by a specific delimiter",
-  "meta.Stream.Source": "Source",
   "meta.Stream.FieldType": "FieldType",
   "meta.Stream.FieldComment": "FieldComment",
-  "meta.Stream.DoubleQuotes": "Double quotes(\")",
   "meta.Stream.DataEncoding": "Data encoding",
-  "meta.Stream.DataStreamOwners": "Owners",
-  "meta.Stream.DataStreamOwnerHelp": "Owners can view and modify data flow information",
-  "meta.Stream.Space": "Space",
-  "meta.Stream.Comma": "Comma(,)",
-  "meta.Stream.DataFlowIntroduction": "Description",
-  "meta.Stream.SourceDataField": "Source data field",
-  "meta.Stream.VerticalLine": "Vertical line (|)",
-  "meta.Stream.File": "File",
-  "meta.Stream.Autonomous": "Auto Push",
+  "meta.Stream.Description": "Description",
+  "meta.Stream.SourceDataField": "Source fields",
+  "meta.Stream.Status.New": "New",
+  "meta.Stream.Status.Pending": "Pending",
+  "meta.Stream.Status.Error": "Error",
+  "meta.Stream.Status.Success": "Success",
   "meta.Consumption.Owner": "Consumption",
   "meta.Consumption.ConsumerGroupName": "Consumer group name",
   "meta.Consumption.ConsumerGroupNameRules": "Only lowercase letters, numbers, minus, and underscores",
@@ -339,17 +341,8 @@
   "pages.GroupDetail.Sink.Status.Pending": "Pending",
   "pages.GroupDetail.Sink.Status.Error": "Error",
   "pages.GroupDetail.Sink.Status.Success": "Success",
-  "pages.GroupDetail.Stream.config.Scale": "Access scale",
-  "pages.GroupDetail.Stream.config.Basic": "Basic Info",
-  "pages.GroupDetail.Stream.config.DataInfo": "Data Info",
-  "pages.GroupDetail.Stream.config.DataStorages": "Data Storages",
-  "pages.GroupDetail.Stream.StreamItemModal.DataFlowConfiguration": "Data stream configuration",
-  "pages.GroupDetail.Stream.Name": "Name",
+  "pages.GroupDetail.Stream.StreamConfigTitle": "Data stream configuration",
   "pages.GroupDetail.Stream.CreateDataStream": "Create",
-  "pages.GroupDetail.Stream.Status.New": "New",
-  "pages.GroupDetail.Stream.Status.Pending": "Pending",
-  "pages.GroupDetail.Stream.Status.Error": "Error",
-  "pages.GroupDetail.Stream.Status.Success": "Success",
   "pages.GroupDetail.PageTitle": "Detail",
   "pages.GroupDetail.Info": "Data Streams Group",
   "pages.GroupDetail.Streams": "DataStreams",
diff --git a/inlong-dashboard/src/metas/clusters/Agent.tsx b/inlong-dashboard/src/metas/clusters/agent.tsx
similarity index 88%
rename from inlong-dashboard/src/metas/clusters/Agent.tsx
rename to inlong-dashboard/src/metas/clusters/agent.tsx
index d199be7c4..f2cf14150 100644
--- a/inlong-dashboard/src/metas/clusters/Agent.tsx
+++ b/inlong-dashboard/src/metas/clusters/agent.tsx
@@ -15,6 +15,6 @@
  * limitations under the License.
  */
 
-import type { ClsConfigItemType } from './common/types';
+import type { FieldItemType } from '@/metas/common';
 
-export const Agent: ClsConfigItemType[] = [];
+export const agent: FieldItemType[] = [];
diff --git a/inlong-dashboard/src/metas/clusters/DataProxy.tsx b/inlong-dashboard/src/metas/clusters/dataProxy.tsx
similarity index 88%
rename from inlong-dashboard/src/metas/clusters/DataProxy.tsx
rename to inlong-dashboard/src/metas/clusters/dataProxy.tsx
index 51076bf9d..5219858a2 100644
--- a/inlong-dashboard/src/metas/clusters/DataProxy.tsx
+++ b/inlong-dashboard/src/metas/clusters/dataProxy.tsx
@@ -17,6 +17,6 @@
  * under the License.
  */
 
-import type { ClsConfigItemType } from './common/types';
+import type { FieldItemType } from '@/metas/common';
 
-export const DataProxy: ClsConfigItemType[] = [];
+export const dataProxy: FieldItemType[] = [];
diff --git a/inlong-dashboard/src/metas/clusters/index.tsx b/inlong-dashboard/src/metas/clusters/index.tsx
index c630aafbc..95d103903 100644
--- a/inlong-dashboard/src/metas/clusters/index.tsx
+++ b/inlong-dashboard/src/metas/clusters/index.tsx
@@ -20,43 +20,37 @@
 import React from 'react';
 import i18n from '@/i18n';
 import UserSelect from '@/components/UserSelect';
-import type { ClsConfigItemType, ClsTableItemType } from './common/types';
-import { DataProxy } from './DataProxy';
-import { Pulsar } from './Pulsar';
-import { TubeMQ } from './TubeMQ';
-import { Agent } from './Agent';
+import type { FieldItemType } from '@/metas/common';
+import { genFields, genForm, genTable } from '@/metas/common';
+import { agent } from './agent';
+import { dataProxy } from './dataProxy';
+import { pulsar } from './pulsar';
+import { tubeMQ } from './tubeMQ';
 
-export interface ClusterItemType {
-  label: string;
-  value: string;
-  config: ClsConfigItemType[];
-  tableColumns: ClsTableItemType[];
-}
-
-const _Clusters: Omit<ClusterItemType, 'tableColumns'>[] = [
+const allClusters = [
   {
     label: 'Agent',
     value: 'AGENT',
-    config: Agent,
+    fields: agent,
   },
   {
     label: 'DataProxy',
     value: 'DATAPROXY',
-    config: DataProxy,
+    fields: dataProxy,
   },
   {
     label: 'Pulsar',
     value: 'PULSAR',
-    config: Pulsar,
+    fields: pulsar,
   },
   {
     label: 'TubeMQ',
     value: 'TUBEMQ',
-    config: TubeMQ,
+    fields: tubeMQ,
   },
 ];
 
-const defaultConfig: ClsConfigItemType[] = [
+const defaultCommonFields: FieldItemType[] = [
   {
     type: 'input',
     label: i18n.t('pages.Clusters.Name'),
@@ -65,16 +59,16 @@ const defaultConfig: ClsConfigItemType[] = [
     props: {
       maxLength: 128,
     },
-    _inTable: true,
+    _renderTable: true,
   },
   {
     type: 'radio',
     name: 'type',
     label: i18n.t('pages.Clusters.Type'),
-    initialValue: _Clusters[0].value,
+    initialValue: allClusters[0].value,
     rules: [{ required: true }],
     props: {
-      options: _Clusters.map(item => ({
+      options: allClusters.map(item => ({
         label: item.label,
         value: item.value,
       })),
@@ -109,14 +103,14 @@ const defaultConfig: ClsConfigItemType[] = [
         },
       },
     },
-    _inTable: true,
+    _renderTable: true,
   },
   {
     type: <UserSelect mode="multiple" />,
     label: i18n.t('pages.Clusters.InCharges'),
     name: 'inCharges',
     rules: [{ required: true }],
-    _inTable: true,
+    _renderTable: true,
   },
   {
     type: 'textarea',
@@ -128,22 +122,14 @@ const defaultConfig: ClsConfigItemType[] = [
   },
 ];
 
-export const Clusters: ClusterItemType[] = _Clusters.map(item => {
-  const config = defaultConfig.concat(item.config);
+export const clusters = allClusters.map(item => {
+  const itemFields = defaultCommonFields.concat(item.fields);
+  const fields = genFields(itemFields);
 
   return {
     ...item,
-    config,
-    tableColumns: config
-      .filter(k => k._inTable)
-      .map(k => {
-        if (typeof k._inTable === 'boolean') {
-          return {
-            title: k.label,
-            dataIndex: k.name,
-          };
-        }
-        return k._inTable;
-      }),
+    fields,
+    form: genForm(fields),
+    table: genTable(fields),
   };
 });
diff --git a/inlong-dashboard/src/metas/clusters/Pulsar.tsx b/inlong-dashboard/src/metas/clusters/pulsar.tsx
similarity index 94%
rename from inlong-dashboard/src/metas/clusters/Pulsar.tsx
rename to inlong-dashboard/src/metas/clusters/pulsar.tsx
index bf9947143..b1cd76276 100644
--- a/inlong-dashboard/src/metas/clusters/Pulsar.tsx
+++ b/inlong-dashboard/src/metas/clusters/pulsar.tsx
@@ -18,9 +18,9 @@
  */
 
 import i18n from '@/i18n';
-import type { ClsConfigItemType } from './common/types';
+import type { FieldItemType } from '@/metas/common';
 
-export const Pulsar: ClsConfigItemType[] = [
+export const pulsar: FieldItemType[] = [
   {
     type: 'input',
     label: 'Service URL',
diff --git a/inlong-dashboard/src/metas/clusters/TubeMQ.tsx b/inlong-dashboard/src/metas/clusters/tubeMQ.tsx
similarity index 93%
rename from inlong-dashboard/src/metas/clusters/TubeMQ.tsx
rename to inlong-dashboard/src/metas/clusters/tubeMQ.tsx
index 7761c2dc3..3a669e324 100644
--- a/inlong-dashboard/src/metas/clusters/TubeMQ.tsx
+++ b/inlong-dashboard/src/metas/clusters/tubeMQ.tsx
@@ -18,9 +18,9 @@
  */
 
 import i18n from '@/i18n';
-import type { ClsConfigItemType } from './common/types';
+import type { FieldItemType } from '@/metas/common';
 
-export const TubeMQ: ClsConfigItemType[] = [
+export const tubeMQ: FieldItemType[] = [
   {
     type: 'input',
     label: 'RPC URL',
diff --git a/inlong-dashboard/src/metas/sinks/hive.tsx b/inlong-dashboard/src/metas/sinks/hive.tsx
index d8806ba8a..bf28d7da4 100644
--- a/inlong-dashboard/src/metas/sinks/hive.tsx
+++ b/inlong-dashboard/src/metas/sinks/hive.tsx
@@ -242,27 +242,27 @@ const getForm: GetStorageFormFieldsType = (
         dropdownMatchSelectWidth: false,
         options: [
           {
-            label: i18n.t('meta.Stream.VerticalLine'),
+            label: i18n.t('meta.Sinks.Hive.DataSeparator.VerticalLine'),
             value: '124',
           },
           {
-            label: i18n.t('meta.Stream.Comma'),
+            label: i18n.t('meta.Sinks.Hive.DataSeparator.Comma'),
             value: '44',
           },
           {
-            label: i18n.t('meta.Stream.DoubleQuotes'),
+            label: i18n.t('meta.Sinks.Hive.DataSeparator.DoubleQuotes'),
             value: '34',
           },
           {
-            label: i18n.t('meta.Stream.Asterisk'),
+            label: i18n.t('meta.Sinks.Hive.DataSeparator.Asterisk'),
             value: '42',
           },
           {
-            label: i18n.t('meta.Stream.Space'),
+            label: i18n.t('meta.Sinks.Hive.DataSeparator.Space'),
             value: '32',
           },
           {
-            label: i18n.t('meta.Stream.Semicolon'),
+            label: i18n.t('meta.Sinks.Hive.DataSeparator.Semicolon'),
             value: '59',
           },
         ],
diff --git a/inlong-dashboard/src/metas/sources/binLog.ts b/inlong-dashboard/src/metas/sources/binLog.ts
index 2428c4523..caa7275e7 100644
--- a/inlong-dashboard/src/metas/sources/binLog.ts
+++ b/inlong-dashboard/src/metas/sources/binLog.ts
@@ -147,27 +147,9 @@ const getForm = (type: 'form' | 'col' = 'form', { currentValues } = {} as any) =
   return type === 'col' ? getColsFromFields(fileds) : fileds;
 };
 
-const toFormValues = data => {
-  return {
-    ...data,
-    _startDumpPosition: data.startDumpPosition ? 1 : 0,
-  };
-};
-
-const toSubmitValues = data => {
-  const output = { ...data };
-  delete output._startDumpPosition;
-  return {
-    ...output,
-    startDumpPosition: data._startDumpPosition ? output.startDumpPosition : null,
-  };
-};
-
 const tableColumns = getForm('col') as ColumnsType;
 
 export const binLog = {
   getForm,
   tableColumns,
-  toFormValues,
-  toSubmitValues,
 };
diff --git a/inlong-dashboard/src/metas/clusters/common/types.ts b/inlong-dashboard/src/metas/stream/extends/index.tsx
similarity index 74%
rename from inlong-dashboard/src/metas/clusters/common/types.ts
rename to inlong-dashboard/src/metas/stream/extends/index.tsx
index 71eb13fb2..e278d9701 100644
--- a/inlong-dashboard/src/metas/clusters/common/types.ts
+++ b/inlong-dashboard/src/metas/stream/extends/index.tsx
@@ -17,11 +17,8 @@
  * under the License.
  */
 
-import type { ColumnType } from 'antd/es/table';
-import type { FormItemProps } from '@/components/FormGenerator';
+import type { FieldItemType } from '@/metas/common';
 
-export interface ClsConfigItemType extends FormItemProps {
-  _inTable?: boolean | ColumnType<unknown>;
-}
-
-export type ClsTableItemType = ColumnType<unknown>;
+export const fieldsExtends: FieldItemType[] = [
+  // You can extended fields here...
+];
diff --git a/inlong-dashboard/src/metas/stream/index.tsx b/inlong-dashboard/src/metas/stream/index.tsx
index f0f136bc8..5fbddffbf 100644
--- a/inlong-dashboard/src/metas/stream/index.tsx
+++ b/inlong-dashboard/src/metas/stream/index.tsx
@@ -17,200 +17,209 @@
  * under the License.
  */
 
-import React from 'react';
-import { FormItemProps } from '@/components/FormGenerator';
-import { pickObjectArray } from '@/utils';
 import EditableTable from '@/components/EditableTable';
 import i18n from '@/i18n';
 import { fieldTypes as sourceFieldsTypes } from '@/metas/sinks/common/sourceFields';
+import type { FieldItemType } from '@/metas/common';
+import { genFields, genForm, genTable } from '@/metas/common';
+import { statusList, genStatusTag } from './status';
+import { fieldsExtends } from './extends';
 
-type RestParams = {
-  // Whether the fieldList is in edit mode
-  fieldListEditing?: boolean;
-};
-
-export default (
-  names: (string | FormItemProps)[],
-  currentValues: Record<string, any> = {},
-  { fieldListEditing = true }: RestParams = {},
-): FormItemProps[] => {
-  const fields: FormItemProps[] = [
-    {
-      type: 'input',
-      label: i18n.t('meta.Stream.DataStreamID'),
-      name: 'inlongStreamId',
-      props: {
-        maxLength: 32,
+const fieldsDefault: FieldItemType[] = [
+  {
+    type: 'input',
+    label: i18n.t('meta.Stream.StreamId'),
+    name: 'inlongStreamId',
+    props: {
+      maxLength: 32,
+    },
+    rules: [
+      { required: true },
+      {
+        pattern: /^[0-9a-z_-]+$/,
+        message: i18n.t('meta.Stream.StreamIdRules'),
       },
-      initialValue: currentValues.inlongStreamId,
-      rules: [
-        { required: true },
+    ],
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Stream.StreamName'),
+    name: 'name',
+    _renderTable: true,
+  },
+  {
+    type: 'textarea',
+    label: i18n.t('meta.Stream.Description'),
+    name: 'description',
+    props: {
+      showCount: true,
+      maxLength: 256,
+    },
+  },
+  {
+    type: 'text',
+    label: i18n.t('basic.Creator'),
+    name: 'creator',
+    visible: false,
+    _renderTable: true,
+  },
+  {
+    type: 'text',
+    label: i18n.t('basic.CreateTime'),
+    name: 'createTime',
+    visible: false,
+    _renderTable: true,
+  },
+  {
+    type: 'select',
+    label: i18n.t('basic.Status'),
+    name: 'status',
+    props: {
+      allowClear: true,
+      options: statusList,
+      dropdownMatchSelectWidth: false,
+    },
+    visible: false,
+    _renderTable: {
+      render: text => genStatusTag(text),
+    },
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Stream.DataType'),
+    name: 'dataType',
+    initialValue: 'CSV',
+    tooltip: i18n.t('meta.Stream.DataTypeCsvHelp'),
+    props: {
+      options: [
+        {
+          label: 'CSV',
+          value: 'CSV',
+        },
         {
-          pattern: /^[0-9a-z_-]+$/,
-          message: i18n.t('meta.Stream.InlongStreamIdRules'),
+          label: 'KEY-VALUE',
+          value: 'KEY-VALUE',
+        },
+        {
+          label: 'JSON',
+          value: 'JSON',
+        },
+        {
+          label: 'AVRO',
+          value: 'AVRO',
         },
       ],
     },
-    {
-      type: 'input',
-      label: i18n.t('meta.Stream.DataStreamName'),
-      name: 'name',
-      initialValue: currentValues.name,
-    },
-    {
-      type: 'textarea',
-      label: i18n.t('meta.Stream.DataFlowIntroduction'),
-      name: 'description',
-      props: {
-        showCount: true,
-        maxLength: 100,
-      },
-      initialValue: currentValues.desc,
+    rules: [{ required: true }],
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Stream.DataEncoding'),
+    name: 'dataEncoding',
+    initialValue: 'UTF-8',
+    props: {
+      options: [
+        {
+          label: 'UTF-8',
+          value: 'UTF-8',
+        },
+        {
+          label: 'GBK',
+          value: 'GBK',
+        },
+      ],
     },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Stream.DataType'),
-      name: 'dataType',
-      initialValue: currentValues.dataType ?? 'CSV',
-      tooltip: i18n.t('meta.Stream.DataTypeCsvHelp'),
-      props: {
-        options: [
-          {
-            label: 'CSV',
-            value: 'CSV',
-          },
-          {
-            label: 'KEY-VALUE',
-            value: 'KEY-VALUE',
-          },
-          {
-            label: 'JSON',
-            value: 'JSON',
-          },
-          {
-            label: 'AVRO',
-            value: 'AVRO',
-          },
-        ],
+    rules: [{ required: true }],
+  },
+  {
+    type: 'select',
+    label: i18n.t('meta.Stream.DataSeparator'),
+    name: 'dataSeparator',
+    initialValue: '124',
+    props: {
+      dropdownMatchSelectWidth: false,
+      options: [
+        {
+          label: i18n.t('meta.Stream.DataSeparator.Space'),
+          value: '32',
+        },
+        {
+          label: i18n.t('meta.Stream.DataSeparator.VerticalLine'),
+          value: '124',
+        },
+        {
+          label: i18n.t('meta.Stream.DataSeparator.Comma'),
+          value: '44',
+        },
+        {
+          label: i18n.t('meta.Stream.DataSeparator.Semicolon'),
+          value: '59',
+        },
+        {
+          label: i18n.t('meta.Stream.DataSeparator.Asterisk'),
+          value: '42',
+        },
+        {
+          label: i18n.t('meta.Stream.DataSeparator.DoubleQuotes'),
+          value: '34',
+        },
+      ],
+      useInput: true,
+      useInputProps: {
+        placeholder: 'ASCII',
       },
-      rules: [{ required: true }],
-      // visible: values => values.dataSourceType !== 'MYSQL_BINLOG',
+      style: { width: 100 },
     },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Stream.DataEncoding'),
-      name: 'dataEncoding',
-      initialValue: currentValues.dataEncoding ?? 'UTF-8',
-      props: {
-        options: [
-          {
-            label: 'UTF-8',
-            value: 'UTF-8',
-          },
-          {
-            label: 'GBK',
-            value: 'GBK',
-          },
-        ],
+    rules: [
+      {
+        required: true,
+        type: 'number',
+        transform: val => +val,
+        min: 0,
+        max: 127,
       },
-      rules: [{ required: true }],
-      // visible: values => values.dataSourceType === 'FILE' || values.dataSourceType === 'AUTO_PUSH',
-    },
-    {
-      type: 'select',
-      label: i18n.t('meta.Stream.fileDelimiter'),
-      name: 'dataSeparator',
-      initialValue: '124',
-      props: {
-        dropdownMatchSelectWidth: false,
-        options: [
-          {
-            label: i18n.t('meta.Stream.Space'),
-            value: '32',
-          },
-          {
-            label: i18n.t('meta.Stream.VerticalLine'),
-            value: '124',
-          },
-          {
-            label: i18n.t('meta.Stream.Comma'),
-            value: '44',
-          },
-          {
-            label: i18n.t('meta.Stream.Semicolon'),
-            value: '59',
-          },
-          {
-            label: i18n.t('meta.Stream.Asterisk'),
-            value: '42',
-          },
-          {
-            label: i18n.t('meta.Stream.DoubleQuotes'),
-            value: '34',
+    ],
+  },
+  {
+    type: EditableTable,
+    label: i18n.t('meta.Stream.SourceDataField'),
+    name: 'rowTypeFields',
+    props: {
+      size: 'small',
+      columns: [
+        {
+          title: i18n.t('meta.Stream.FieldName'),
+          dataIndex: 'fieldName',
+          rules: [
+            { required: true },
+            {
+              pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/,
+              message: i18n.t('meta.Stream.FieldNameRule'),
+            },
+          ],
+        },
+        {
+          title: i18n.t('meta.Stream.FieldType'),
+          dataIndex: 'fieldType',
+          type: 'select',
+          initialValue: sourceFieldsTypes[0].value,
+          props: {
+            options: sourceFieldsTypes,
           },
-        ],
-        useInput: true,
-        useInputProps: {
-          placeholder: 'ASCII',
+          rules: [{ required: true }],
         },
-        style: { width: 100 },
-      },
-      rules: [
         {
-          required: true,
-          type: 'number',
-          transform: val => +val,
-          min: 0,
-          max: 127,
+          title: i18n.t('meta.Stream.FieldComment'),
+          dataIndex: 'fieldComment',
         },
       ],
-      // visible: values =>
-      //   (values.dataSourceType === 'FILE' || values.dataSourceType === 'AUTO_PUSH') &&
-      //   values.dataType === 'CSV',
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          editing={fieldListEditing}
-          columns={[
-            {
-              title: i18n.t('meta.Stream.FieldName'),
-              dataIndex: 'fieldName',
-              props: () => ({
-                disabled: !fieldListEditing,
-              }),
-              rules: [
-                { required: true },
-                {
-                  pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/,
-                  message: i18n.t('meta.Stream.FieldNameRule'),
-                },
-              ],
-            },
-            {
-              title: i18n.t('meta.Stream.FieldType'),
-              dataIndex: 'fieldType',
-              type: 'select',
-              initialValue: sourceFieldsTypes[0].value,
-              props: () => ({
-                disabled: !fieldListEditing,
-                options: sourceFieldsTypes,
-              }),
-              rules: [{ required: true }],
-            },
-            {
-              title: i18n.t('meta.Stream.FieldComment'),
-              dataIndex: 'fieldComment',
-            },
-          ]}
-        />
-      ),
-      label: i18n.t('meta.Stream.SourceDataField'),
-      name: 'rowTypeFields',
-      visible: () => !(currentValues.dataType as string[])?.includes('PB'),
     },
-  ];
+  },
+];
+
+export const stream = genFields(fieldsDefault, fieldsExtends);
+
+export const streamForm = genForm(stream);
 
-  return pickObjectArray(names, fields);
-};
+export const streamTable = genTable(stream);
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStream/status.tsx b/inlong-dashboard/src/metas/stream/status.tsx
similarity index 87%
rename from inlong-dashboard/src/pages/GroupDetail/DataStream/status.tsx
rename to inlong-dashboard/src/metas/stream/status.tsx
index bbe94680c..ffe944dd8 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataStream/status.tsx
+++ b/inlong-dashboard/src/metas/stream/status.tsx
@@ -31,23 +31,23 @@ type StatusProp = {
 
 export const statusList: StatusProp[] = [
   {
-    label: i18n.t('pages.GroupDetail.Stream.Status.New'),
+    label: i18n.t('meta.Stream.Status.New'),
     value: 100,
     type: 'default',
   },
   {
-    label: i18n.t('pages.GroupDetail.Stream.Status.Pending'),
+    label: i18n.t('meta.Stream.Status.Pending'),
     value: 110,
     type: 'primary',
     icon: <ClockCircleFilled />,
   },
   {
-    label: i18n.t('pages.GroupDetail.Stream.Status.Error'),
+    label: i18n.t('meta.Stream.Status.Error'),
     value: 120,
     type: 'error',
   },
   {
-    label: i18n.t('pages.GroupDetail.Stream.Status.Success'),
+    label: i18n.t('meta.Stream.Status.Success'),
     value: 130,
     type: 'success',
   },
diff --git a/inlong-dashboard/src/pages/ClusterTags/ClusterList.tsx b/inlong-dashboard/src/pages/ClusterTags/ClusterList.tsx
index a6e08cf95..490192788 100644
--- a/inlong-dashboard/src/pages/ClusterTags/ClusterList.tsx
+++ b/inlong-dashboard/src/pages/ClusterTags/ClusterList.tsx
@@ -23,7 +23,7 @@ import i18n from '@/i18n';
 import HighTable from '@/components/HighTable';
 import { defaultSize } from '@/configs/pagination';
 import { useRequest } from '@/hooks';
-import { Clusters } from '@/metas/clusters';
+import { clusters } from '@/metas/clusters';
 import ClusterBindModal from './ClusterBindModal';
 import request from '@/utils/request';
 
@@ -35,6 +35,7 @@ const getFilterFormContent = defaultValues => [
   {
     type: 'inputsearch',
     name: 'keyword',
+    initialValue: defaultValues.keyword,
   },
   {
     type: 'select',
@@ -42,27 +43,18 @@ const getFilterFormContent = defaultValues => [
     label: i18n.t('pages.Clusters.Type'),
     initialValue: defaultValues.type,
     props: {
-      options: [
-        {
-          label: i18n.t('pages.Clusters.TypeAll'),
-          value: '',
-        },
-      ].concat(
-        Clusters.map(item => ({
-          label: item.label,
-          value: item.value,
-        })),
-      ),
+      allowClear: true,
+      options: clusters,
     },
   },
 ];
 
 const Comp: React.FC<ClusterListProps> = ({ clusterTag }) => {
   const [options, setOptions] = useState({
-    keyword: '',
+    keyword: undefined,
     pageSize: defaultSize,
     pageNum: 1,
-    type: '',
+    type: undefined,
   });
 
   const [clusterBindModal, setClusterBindModal] = useState<Record<string, unknown>>({
diff --git a/inlong-dashboard/src/pages/Clusters/CreateModal.tsx b/inlong-dashboard/src/pages/Clusters/CreateModal.tsx
index 1bafc2ca2..6e8ff38c8 100644
--- a/inlong-dashboard/src/pages/Clusters/CreateModal.tsx
+++ b/inlong-dashboard/src/pages/Clusters/CreateModal.tsx
@@ -23,7 +23,7 @@ import { ModalProps } from 'antd/es/modal';
 import FormGenerator, { useForm } from '@/components/FormGenerator';
 import { useRequest, useUpdateEffect } from '@/hooks';
 import request from '@/utils/request';
-import { Clusters } from '@/metas/clusters';
+import { clusters } from '@/metas/clusters';
 import i18n from '@/i18n';
 
 export interface Props extends ModalProps {
@@ -34,7 +34,7 @@ export interface Props extends ModalProps {
 const Comp: React.FC<Props> = ({ id, ...modalProps }) => {
   const [form] = useForm();
 
-  const [type, setType] = useState(Clusters[0].value);
+  const [type, setType] = useState(clusters[0].value);
 
   const { data: savedData, run: getData } = useRequest(
     id => ({
@@ -82,13 +82,13 @@ const Comp: React.FC<Props> = ({ id, ...modalProps }) => {
       }
     } else {
       form.resetFields();
-      setType(Clusters[0].value);
+      setType(clusters[0].value);
     }
   }, [modalProps.visible]);
 
   const content = useMemo(() => {
-    const current = Clusters.find(item => item.value === type);
-    return current?.config;
+    const current = clusters.find(item => item.value === type);
+    return current?.form;
   }, [type]);
 
   return (
diff --git a/inlong-dashboard/src/pages/Clusters/index.tsx b/inlong-dashboard/src/pages/Clusters/index.tsx
index eceffa5b4..594b79b9a 100644
--- a/inlong-dashboard/src/pages/Clusters/index.tsx
+++ b/inlong-dashboard/src/pages/Clusters/index.tsx
@@ -25,7 +25,7 @@ import HighTable from '@/components/HighTable';
 import { PageContainer } from '@/components/PageContainer';
 import { defaultSize } from '@/configs/pagination';
 import { useRequest } from '@/hooks';
-import { Clusters } from '@/metas/clusters';
+import { clusters } from '@/metas/clusters';
 import CreateModal from './CreateModal';
 import request from '@/utils/request';
 import { timestampFormat } from '@/utils';
@@ -42,7 +42,7 @@ const getFilterFormContent = defaultValues => [
     initialValue: defaultValues.type,
     props: {
       buttonStyle: 'solid',
-      options: Clusters.map(item => ({
+      options: clusters.map(item => ({
         label: item.label,
         value: item.value,
       })),
@@ -55,7 +55,7 @@ const Comp: React.FC = () => {
     keyword: '',
     pageSize: defaultSize,
     pageNum: 1,
-    type: Clusters[0].value,
+    type: clusters[0].value,
   });
 
   const [createModal, setCreateModal] = useState<Record<string, unknown>>({
@@ -123,47 +123,42 @@ const Comp: React.FC = () => {
   };
 
   const columns = useMemo(() => {
-    const current = Clusters.find(item => item.value === options.type);
-    if (!current?.tableColumns) return [];
+    const current = clusters.find(item => item.value === options.type);
+    if (!current?.table) return [];
 
-    return current.tableColumns
-      .map(item => ({
-        ...item,
-        ellipsisMulti: 2,
-      }))
-      .concat([
-        {
-          title: i18n.t('pages.Clusters.LastModifier'),
-          dataIndex: 'modifier',
-          width: 150,
-          render: (text, record: any) => (
-            <>
-              <div>{text}</div>
-              <div>{record.modifyTime && timestampFormat(record.modifyTime)}</div>
-            </>
-          ),
-        },
-        {
-          title: i18n.t('basic.Operating'),
-          dataIndex: 'action',
-          width: 200,
-          render: (text, record) => (
-            <>
-              {(record.type === 'DATAPROXY' || record.type === 'AGENT') && (
-                <Link to={`/clusters/node?type=${record.type}&clusterId=${record.id}`}>
-                  {i18n.t('pages.Clusters.Node.Name')}
-                </Link>
-              )}
-              <Button type="link" onClick={() => onEdit(record)}>
-                {i18n.t('basic.Edit')}
-              </Button>
-              <Button type="link" onClick={() => onDelete(record)}>
-                {i18n.t('basic.Delete')}
-              </Button>
-            </>
-          ),
-        } as any,
-      ]);
+    return current.table.concat([
+      {
+        title: i18n.t('pages.Clusters.LastModifier'),
+        dataIndex: 'modifier',
+        width: 150,
+        render: (text, record: any) => (
+          <>
+            <div>{text}</div>
+            <div>{record.modifyTime && timestampFormat(record.modifyTime)}</div>
+          </>
+        ),
+      },
+      {
+        title: i18n.t('basic.Operating'),
+        dataIndex: 'action',
+        width: 200,
+        render: (text, record) => (
+          <>
+            {(record.type === 'DATAPROXY' || record.type === 'AGENT') && (
+              <Link to={`/clusters/node?type=${record.type}&clusterId=${record.id}`}>
+                {i18n.t('pages.Clusters.Node.Name')}
+              </Link>
+            )}
+            <Button type="link" onClick={() => onEdit(record)}>
+              {i18n.t('basic.Edit')}
+            </Button>
+            <Button type="link" onClick={() => onDelete(record)}>
+              {i18n.t('basic.Delete')}
+            </Button>
+          </>
+        ),
+      } as any,
+    ]);
   }, [options.type, onDelete]);
 
   return (
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStream/StreamItemModal.tsx b/inlong-dashboard/src/pages/GroupDetail/DataStream/StreamItemModal.tsx
index 85eae5f11..a2c32dd9d 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataStream/StreamItemModal.tsx
+++ b/inlong-dashboard/src/pages/GroupDetail/DataStream/StreamItemModal.tsx
@@ -18,13 +18,13 @@
  */
 
 import React from 'react';
-import { Divider, Modal, message } from 'antd';
+import { Modal, message } from 'antd';
 import { ModalProps } from 'antd/es/modal';
 import FormGenerator, { useForm } from '@/components/FormGenerator';
 import { useUpdateEffect, useRequest } from '@/hooks';
 import i18n from '@/i18n';
 import { groupForm } from '@/metas/group';
-import getStreamFields from '@/metas/stream';
+import { streamForm } from '@/metas/stream';
 import request from '@/utils/request';
 import { pickObjectArray } from '@/utils';
 import { dataToValues, valuesToData } from './helper';
@@ -38,31 +38,7 @@ export interface Props extends ModalProps {
 
 export const genFormContent = (isCreate, mqType) => {
   return [
-    ...getStreamFields([
-      {
-        type: (
-          <Divider orientation="left">{i18n.t('pages.GroupDetail.Stream.config.Basic')}</Divider>
-        ),
-      },
-      'inlongStreamId',
-      'name',
-      'description',
-      {
-        type: (
-          <Divider orientation="left">{i18n.t('pages.GroupDetail.Stream.config.DataInfo')}</Divider>
-        ),
-      },
-      'dataType',
-      'dataEncoding',
-      'dataSeparator',
-      'rowTypeFields',
-      {
-        type: (
-          <Divider orientation="left">{i18n.t('pages.GroupDetail.Stream.config.Scale')}</Divider>
-        ),
-        visible: mqType === 'PULSAR',
-      },
-    ]),
+    ...streamForm,
     ...pickObjectArray(['dailyRecords', 'dailyStorage', 'peakRecords', 'maxLength'], groupForm).map(
       item => ({
         ...item,
@@ -93,21 +69,23 @@ const Comp: React.FC<Props> = ({ inlongGroupId, inlongStreamId, mqType, ...modal
     },
     {
       manual: true,
-      onSuccess: result => form.setFieldsValue(dataToValues([result])?.[0]),
+      onSuccess: result => form.setFieldsValue(dataToValues(result)),
     },
   );
 
   const onOk = async () => {
-    const values = {
-      ...savedData,
-      ...(await form.validateFields()),
-    };
+    const isUpdate = !!inlongStreamId;
+    const values = await form.validateFields();
+    const submitData = valuesToData(values, inlongGroupId);
+    if (isUpdate) {
+      submitData.id = savedData?.id;
+      submitData.version = savedData?.version;
+    }
 
-    const submitData = valuesToData(values ? [values] : [], inlongGroupId);
     await request({
-      url: inlongStreamId ? '/stream/update' : '/stream/save',
+      url: isUpdate ? '/stream/update' : '/stream/save',
       method: 'POST',
-      data: submitData?.[0],
+      data: submitData,
     });
     await modalProps?.onOk(values);
     message.success(i18n.t('basic.OperatingSuccess'));
@@ -126,7 +104,7 @@ const Comp: React.FC<Props> = ({ inlongGroupId, inlongStreamId, mqType, ...modal
   return (
     <Modal
       {...modalProps}
-      title={i18n.t('pages.GroupDetail.Stream.StreamItemModal.DataFlowConfiguration')}
+      title={i18n.t('pages.GroupDetail.Stream.StreamConfigTitle')}
       width={1000}
       onOk={onOk}
     >
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStream/config.tsx b/inlong-dashboard/src/pages/GroupDetail/DataStream/config.tsx
index b1f4fa19a..9641bacd1 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataStream/config.tsx
+++ b/inlong-dashboard/src/pages/GroupDetail/DataStream/config.tsx
@@ -17,8 +17,8 @@
  * under the License.
  */
 
-import i18n from '@/i18n';
-import { statusList } from './status';
+import { streamForm } from '@/metas/stream';
+import { pickObjectArray } from '@/utils';
 
 export const getFilterFormContent = (defaultValues = {} as any) => [
   {
@@ -26,13 +26,9 @@ export const getFilterFormContent = (defaultValues = {} as any) => [
     name: 'keyword',
     initialValue: defaultValues.keyword,
   },
-  {
-    type: 'select',
-    name: 'status',
-    label: i18n.t('basic.Status'),
-    props: {
-      allowClear: true,
-      options: statusList,
-    },
-  },
+  ...pickObjectArray(['status'], streamForm).map(item => ({
+    ...item,
+    visible: true,
+    initialValue: defaultValues[item.name],
+  })),
 ];
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStream/helper.ts b/inlong-dashboard/src/pages/GroupDetail/DataStream/helper.ts
index 6f77c642b..01e86e5bf 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataStream/helper.ts
+++ b/inlong-dashboard/src/pages/GroupDetail/DataStream/helper.ts
@@ -19,53 +19,44 @@
 
 // Convert form data into interface submission data format
 export const valuesToData = (values, inlongGroupId) => {
-  const array = values.map(item => {
-    const { inlongStreamId, predefinedFields = [], rowTypeFields = [], version, ...rest } = item;
+  const { inlongStreamId, predefinedFields = [], rowTypeFields = [], version, ...rest } = values;
 
-    const fieldList = predefinedFields.concat(rowTypeFields).map((item, idx) => ({
-      ...item,
-      inlongGroupId,
-      inlongStreamId,
-      isPredefinedField: idx < predefinedFields.length ? 1 : 0,
-    }));
+  const fieldList = predefinedFields.concat(rowTypeFields).map((item, idx) => ({
+    ...item,
+    inlongGroupId,
+    inlongStreamId,
+    isPredefinedField: idx < predefinedFields.length ? 1 : 0,
+  }));
 
-    const output = {
-      ...rest,
-      inlongGroupId,
-      inlongStreamId,
-      version,
-    };
+  const output = {
+    ...rest,
+    inlongGroupId,
+    inlongStreamId,
+    version,
+  };
 
-    if (fieldList?.length) output.fieldList = fieldList;
+  if (fieldList?.length) output.fieldList = fieldList;
 
-    return output;
-  });
-
-  return array;
+  return output;
 };
 
 // Convert interface data to form data
 export const dataToValues = data => {
-  const array = data.map(item => {
-    const fieldList = item?.fieldList?.reduce(
-      (acc, cur) => {
-        cur.isPredefinedField ? acc.predefinedFields.push(cur) : acc.rowTypeFields.push(cur);
-        return acc;
-      },
-      {
-        predefinedFields: [],
-        rowTypeFields: [],
-      },
-    );
-
-    const output = {
-      hasHigher: false,
-      ...item,
-      ...fieldList,
-    };
-
-    return output;
-  });
-
-  return array;
+  const fieldList = data?.fieldList?.reduce(
+    (acc, cur) => {
+      cur.isPredefinedField ? acc.predefinedFields.push(cur) : acc.rowTypeFields.push(cur);
+      return acc;
+    },
+    {
+      predefinedFields: [],
+      rowTypeFields: [],
+    },
+  );
+
+  const output = {
+    ...data,
+    ...fieldList,
+  };
+
+  return output;
 };
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx b/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx
index 60efdd0b9..e17cf2d09 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx
+++ b/inlong-dashboard/src/pages/GroupDetail/DataStream/index.tsx
@@ -24,10 +24,10 @@ import { defaultSize } from '@/configs/pagination';
 import { useRequest } from '@/hooks';
 import request from '@/utils/request';
 import { useTranslation } from 'react-i18next';
+import { streamTable } from '@/metas/stream';
 import { CommonInterface } from '../common';
 import StreamItemModal from './StreamItemModal';
 import { getFilterFormContent } from './config';
-import { genStatusTag } from './status';
 
 type Props = CommonInterface;
 
@@ -79,8 +79,8 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => {
     }));
   };
 
-  const onEdit = ({ inlongStreamId }) => {
-    setStreamItemModal(prev => ({ ...prev, visible: true, inlongStreamId }));
+  const onEdit = record => {
+    setStreamItemModal(prev => ({ ...prev, visible: true, inlongStreamId: record.inlongStreamId }));
   };
 
   const onDelete = record => {
@@ -123,28 +123,7 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => {
     total: data?.total,
   };
 
-  const columns = [
-    {
-      title: 'ID',
-      dataIndex: 'inlongStreamId',
-    },
-    {
-      title: t('pages.GroupDetail.Stream.Name'),
-      dataIndex: 'name',
-    },
-    {
-      title: t('basic.Creator'),
-      dataIndex: 'creator',
-    },
-    {
-      title: t('basic.CreateTime'),
-      dataIndex: 'createTime',
-    },
-    {
-      title: t('basic.Status'),
-      dataIndex: 'status',
-      render: text => genStatusTag(text),
-    },
+  const columns = streamTable.concat([
     {
       title: t('basic.Operating'),
       dataIndex: 'action',
@@ -162,7 +141,7 @@ const Comp = ({ inlongGroupId, readonly, mqType }: Props, ref) => {
           </>
         ),
     },
-  ];
+  ]);
 
   return (
     <>