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/15 11:54:46 UTC

[inlong] branch master updated: [INLONG-5901][Dashboard] Unified sinks type definition (#5902)

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 175b90eb1 [INLONG-5901][Dashboard] Unified sinks type definition (#5902)
175b90eb1 is described below

commit 175b90eb107a1eeada09c21180ea46c1e24e6a0e
Author: Daniel <le...@apache.org>
AuthorDate: Thu Sep 15 19:54:40 2022 +0800

    [INLONG-5901][Dashboard] Unified sinks type definition (#5902)
---
 inlong-dashboard/package-lock.json                 | 185 ++++++-
 inlong-dashboard/package.json                      |   2 +-
 .../src/components/FormGenerator/FormGenerator.tsx |  82 ++-
 .../components/FormGenerator/FormItemContent.tsx   | 244 ++++-----
 inlong-dashboard/src/metas/sinks/clickhouse.tsx    | 460 ++++++++---------
 inlong-dashboard/src/metas/sinks/common/status.tsx |  68 +++
 inlong-dashboard/src/metas/sinks/es.tsx            | 282 +++++------
 inlong-dashboard/src/metas/sinks/greenplum.tsx     | 202 ++++----
 inlong-dashboard/src/metas/sinks/hbase.tsx         | 240 ++++-----
 inlong-dashboard/src/metas/sinks/hive.tsx          | 555 ++++++++++-----------
 inlong-dashboard/src/metas/sinks/iceberg.tsx       | 337 ++++++-------
 inlong-dashboard/src/metas/sinks/index.ts          | 123 +++--
 inlong-dashboard/src/metas/sinks/kafka.tsx         | 188 ++++---
 inlong-dashboard/src/metas/sinks/mysql.tsx         | 201 ++++----
 inlong-dashboard/src/metas/sinks/oracle.tsx        | 202 ++++----
 inlong-dashboard/src/metas/sinks/postgreSql.tsx    | 227 ++++-----
 inlong-dashboard/src/metas/sinks/sqlServer.tsx     | 283 +++++------
 .../src/metas/sinks/tdsqlPostgreSql.tsx            | 227 ++++-----
 inlong-dashboard/src/metas/sources/index.ts        |   2 +-
 .../src/pages/GroupDetail/DataSources/index.tsx    |  15 +-
 .../pages/GroupDetail/DataStorage/DetailModal.tsx  | 108 ++--
 .../src/pages/GroupDetail/DataStorage/index.tsx    |  42 +-
 inlong-dashboard/src/themes/cover.less             |  13 -
 23 files changed, 2057 insertions(+), 2231 deletions(-)

diff --git a/inlong-dashboard/package-lock.json b/inlong-dashboard/package-lock.json
index fdedbf2b5..3139b8085 100644
--- a/inlong-dashboard/package-lock.json
+++ b/inlong-dashboard/package-lock.json
@@ -1703,6 +1703,13 @@
       "integrity": "sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==",
       "dev": true
     },
+    "@esbuild/linux-loong64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz",
+      "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==",
+      "dev": true,
+      "optional": true
+    },
     "@eslint/eslintrc": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
@@ -4874,6 +4881,7 @@
       "requires": {
         "anymatch": "~3.1.2",
         "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
         "glob-parent": "~5.1.2",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
@@ -6107,8 +6115,77 @@
       "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==",
       "dev": true,
       "requires": {
-        "esbuild-linux-64": "0.14.54"
-      }
+        "@esbuild/linux-loong64": "0.14.54",
+        "esbuild-android-64": "0.14.54",
+        "esbuild-android-arm64": "0.14.54",
+        "esbuild-darwin-64": "0.14.54",
+        "esbuild-darwin-arm64": "0.14.54",
+        "esbuild-freebsd-64": "0.14.54",
+        "esbuild-freebsd-arm64": "0.14.54",
+        "esbuild-linux-32": "0.14.54",
+        "esbuild-linux-64": "0.14.54",
+        "esbuild-linux-arm": "0.14.54",
+        "esbuild-linux-arm64": "0.14.54",
+        "esbuild-linux-mips64le": "0.14.54",
+        "esbuild-linux-ppc64le": "0.14.54",
+        "esbuild-linux-riscv64": "0.14.54",
+        "esbuild-linux-s390x": "0.14.54",
+        "esbuild-netbsd-64": "0.14.54",
+        "esbuild-openbsd-64": "0.14.54",
+        "esbuild-sunos-64": "0.14.54",
+        "esbuild-windows-32": "0.14.54",
+        "esbuild-windows-64": "0.14.54",
+        "esbuild-windows-arm64": "0.14.54"
+      }
+    },
+    "esbuild-android-64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz",
+      "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-android-arm64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz",
+      "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-darwin-64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz",
+      "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-darwin-arm64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz",
+      "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-freebsd-64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz",
+      "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-freebsd-arm64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz",
+      "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-32": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz",
+      "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==",
+      "dev": true,
+      "optional": true
     },
     "esbuild-linux-64": {
       "version": "0.14.54",
@@ -6117,6 +6194,90 @@
       "dev": true,
       "optional": true
     },
+    "esbuild-linux-arm": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz",
+      "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-arm64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz",
+      "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-mips64le": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz",
+      "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-ppc64le": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz",
+      "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-riscv64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz",
+      "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-linux-s390x": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz",
+      "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-netbsd-64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz",
+      "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-openbsd-64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz",
+      "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-sunos-64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz",
+      "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-windows-32": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz",
+      "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-windows-64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz",
+      "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==",
+      "dev": true,
+      "optional": true
+    },
+    "esbuild-windows-arm64": {
+      "version": "0.14.54",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz",
+      "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==",
+      "dev": true,
+      "optional": true
+    },
     "escalade": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -7261,6 +7422,13 @@
       "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
       "dev": true
     },
+    "fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "optional": true
+    },
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -8783,6 +8951,7 @@
         "@types/node": "*",
         "anymatch": "^3.0.3",
         "fb-watchman": "^2.0.0",
+        "fsevents": "^2.3.2",
         "graceful-fs": "^4.2.9",
         "jest-regex-util": "^27.5.1",
         "jest-serializer": "^27.5.1",
@@ -12933,6 +13102,7 @@
         "eslint-webpack-plugin": "^3.1.1",
         "file-loader": "^6.2.0",
         "fs-extra": "^10.0.0",
+        "fsevents": "^2.3.2",
         "html-webpack-plugin": "^5.5.0",
         "identity-obj-proxy": "^3.0.0",
         "jest": "^27.4.3",
@@ -13481,7 +13651,10 @@
       "version": "2.78.1",
       "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz",
       "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==",
-      "dev": true
+      "dev": true,
+      "requires": {
+        "fsevents": "~2.3.2"
+      }
     },
     "rollup-plugin-terser": {
       "version": "7.0.2",
@@ -15224,6 +15397,7 @@
       "dev": true,
       "requires": {
         "esbuild": "^0.14.47",
+        "fsevents": "~2.3.2",
         "postcss": "^8.4.16",
         "resolve": "^1.22.1",
         "rollup": ">=2.75.6 <2.77.0 || ~2.77.0"
@@ -15233,7 +15407,10 @@
           "version": "2.77.3",
           "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz",
           "integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==",
-          "dev": true
+          "dev": true,
+          "requires": {
+            "fsevents": "~2.3.2"
+          }
         }
       }
     },
diff --git a/inlong-dashboard/package.json b/inlong-dashboard/package.json
index aed35bae0..790e2b3b3 100644
--- a/inlong-dashboard/package.json
+++ b/inlong-dashboard/package.json
@@ -19,7 +19,7 @@
     "react-redux": "^7.2.0",
     "react-router-dom": "^5.2.0",
     "redux": "^4.0.5",
-    "umi-request": "^1.3.5"
+    "umi-request": "^1.4.0"
   },
   "scripts": {
     "analyze": "source-map-explorer 'build/static/js/*.js'",
diff --git a/inlong-dashboard/src/components/FormGenerator/FormGenerator.tsx b/inlong-dashboard/src/components/FormGenerator/FormGenerator.tsx
index ba120a6b0..5d9971e9c 100644
--- a/inlong-dashboard/src/components/FormGenerator/FormGenerator.tsx
+++ b/inlong-dashboard/src/components/FormGenerator/FormGenerator.tsx
@@ -17,11 +17,12 @@
  * under the License.
  */
 
-import React, { useState, useEffect, useCallback } from 'react';
-import merge from 'lodash/merge';
-import { trim } from '@/utils';
+import React, { useState, useEffect, useMemo } from 'react';
 import { Form } from 'antd';
 import type { FormProps } from 'antd';
+import merge from 'lodash/merge';
+import { usePersistFn } from '@/hooks';
+import { trim } from '@/utils';
 import FormItemContent, { FormItemProps as ItemType } from './FormItemContent';
 
 export interface FormItemProps extends Omit<ItemType, 'props'> {
@@ -73,7 +74,7 @@ const FormGenerator: React.FC<FormGeneratorProps> = props => {
 
   const viewOnly = props.viewOnly ?? false;
 
-  const combineContentWithProps = useCallback(
+  const combineContentWithProps = usePersistFn(
     (initialContent: Record<string, any>[], props: FormGeneratorProps) => {
       return initialContent.map((v: any) => {
         const initialProps =
@@ -132,7 +133,6 @@ const FormGenerator: React.FC<FormGeneratorProps> = props => {
         };
       });
     },
-    [realTimeValues, form, viewOnly],
   );
 
   // A real-time value is generated when it is first mounted, because the initialValue may be defined on the FormItem
@@ -168,25 +168,7 @@ const FormGenerator: React.FC<FormGeneratorProps> = props => {
     }
   }, [props.content, props.contents, props, combineContentWithProps]);
 
-  const { layout = 'horizontal', useMaxWidth } = props;
-  const isHorizontal = layout === 'horizontal';
-  const {
-    labelCol = isHorizontal && !props.labelCol ? { span: 6 } : props.labelCol,
-    wrapperCol = isHorizontal && !props.labelCol ? { span: 18 } : props.wrapperCol,
-    labelAlign = 'left',
-    style = useMaxWidth
-      ? {
-          ...props.style,
-          maxWidth: typeof useMaxWidth === 'number' ? useMaxWidth : 1200,
-        }
-      : props.style,
-    children,
-    onFilter,
-  } = props;
-
-  const isInline = layout === 'inline';
-
-  const onValuesChange = (changedValues, allValues) => {
+  const onValuesChange = usePersistFn((changedValues, allValues) => {
     props.onValuesChange && props.onValuesChange(changedValues, allValues);
 
     if (contents && contents.length) {
@@ -197,42 +179,48 @@ const FormGenerator: React.FC<FormGeneratorProps> = props => {
       });
       const newRealTimeValues = trim(allValues) as any;
       setRealTimeValues(newRealTimeValues);
-      if (noPrevent && onFilter) {
-        onFilter(newRealTimeValues);
+      if (noPrevent && props.onFilter) {
+        props.onFilter(newRealTimeValues);
       }
     }
-  };
-
-  const formProps = { ...props };
-  delete formProps.useMaxWidth;
-  delete formProps.content;
-  delete formProps.contents;
-  delete formProps.onFilter;
-  delete formProps.initialValues;
+  });
+
+  const formProps = useMemo(() => {
+    const { layout = 'horizontal', useMaxWidth } = props;
+    const isHorizontal = layout === 'horizontal';
+    const {
+      labelCol = isHorizontal && !props.labelCol ? { span: 6 } : props.labelCol,
+      wrapperCol = isHorizontal && !props.labelCol ? { span: 18 } : props.wrapperCol,
+      labelAlign = 'left',
+      style = useMaxWidth
+        ? {
+            ...props.style,
+            maxWidth: typeof useMaxWidth === 'number' ? useMaxWidth : 1200,
+          }
+        : props.style,
+    } = props;
+    const obj = { ...props, labelCol, wrapperCol, labelAlign, style };
+    delete obj.useMaxWidth;
+    delete obj.content;
+    delete obj.contents;
+    delete obj.onFilter;
+    delete obj.initialValues;
+    return obj;
+  }, [props]);
 
   return (
-    <Form
-      {...formProps}
-      requiredMark={!viewOnly}
-      form={form}
-      layout={layout}
-      labelCol={labelCol}
-      wrapperCol={wrapperCol}
-      labelAlign={labelAlign}
-      style={style}
-      onValuesChange={onValuesChange}
-    >
+    <Form {...formProps} requiredMark={!viewOnly} form={form} onValuesChange={onValuesChange}>
       {contents &&
         contents.map((val: ContentsItemProps, index) => (
           <FormItemContent
             content={val.content}
             target={val.target}
-            useInline={isInline}
+            useInline={formProps.layout === 'inline'}
             key={index}
             values={realTimeValues}
           />
         ))}
-      {children}
+      {props.children}
     </Form>
   );
 };
diff --git a/inlong-dashboard/src/components/FormGenerator/FormItemContent.tsx b/inlong-dashboard/src/components/FormGenerator/FormItemContent.tsx
index 4a1b86c1e..b484be49e 100644
--- a/inlong-dashboard/src/components/FormGenerator/FormItemContent.tsx
+++ b/inlong-dashboard/src/components/FormGenerator/FormItemContent.tsx
@@ -17,10 +17,11 @@
  * under the License.
  */
 
-import React, { useMemo } from 'react';
+import React, { useMemo, useState } from 'react';
 import { createPortal } from 'react-dom';
 import { Col, Row, Form, Space } from 'antd';
 import { FormItemProps as AntdFormItemProps } from 'antd/lib/form';
+import TextSwitch from '@/components/TextSwitch';
 import plugins from './plugins';
 
 type PluginsTypes = keyof typeof plugins;
@@ -38,6 +39,7 @@ export interface FormItemProps extends AntdFormItemProps {
   suffix?: React.ReactNode | SuffixDefineType;
   // The extra name will not be rendered on the page, but will be automatically collected when the form getValues
   extraNames?: string[];
+  isPro?: boolean;
 }
 
 export interface FormItemContentProps {
@@ -51,133 +53,141 @@ export interface FormItemContentProps {
   values?: Record<string, unknown>;
 }
 
+// Form atomic component
+const Comp = ({ type, ...props }: { type: PluginsTypes }) => {
+  const Comp = plugins[(type as string) || 'input'];
+  return <Comp {...props} />;
+};
+
+const FormItem = ({ type: T, formItemProps, useSpace, props }) => {
+  return (
+    <Form.Item {...formItemProps} noStyle={useSpace}>
+      {typeof T === 'string' ? (
+        <Comp type={T} {...props} />
+      ) : React.isValidElement(T) ? (
+        T
+      ) : (
+        <T {...props} />
+      )}
+    </Form.Item>
+  );
+};
+
+const Content = ({ useSpace, suffix, extra, children, label, required, style }) => {
+  return useSpace ? (
+    <Form.Item label={label} required={required} style={style} extra={extra}>
+      <Space>
+        {children}
+        {suffix}
+      </Space>
+    </Form.Item>
+  ) : (
+    children
+  );
+};
+
 const FormItemContent: React.FC<FormItemContentProps> = ({
   content,
   useInline = false,
   target,
   values = {},
 }) => {
-  // Form atomic component
-  const Comp = useMemo(
-    () =>
-      ({ type, ...props }: { type: PluginsTypes }) => {
-        const Comp = plugins[(type as string) || 'input'];
-        return <Comp {...props} />;
-      },
-    [],
-  );
+  const [proOpened, setProOpened] = useState(false);
 
-  const FormItem = useMemo(
-    () =>
-      ({ type: T, formItemProps, useSpace, props }) => {
-        return (
-          <Form.Item {...formItemProps} noStyle={useSpace}>
-            {typeof T === 'string' ? (
-              <Comp type={T} {...props} />
-            ) : React.isValidElement(T) ? (
-              T
-            ) : (
-              <T {...props} />
-            )}
-          </Form.Item>
-        );
-      },
-    // eslint-disable-next-line
-    [],
-  );
+  const body = useMemo(() => {
+    let proIndex = -1;
+    const conts = content.map(
+      (
+        {
+          visible = true,
+          type = 'text',
+          col,
+          suffix,
+          props,
+          extraNames = [],
+          isPro,
+          ...formItemProps
+        },
+        index,
+      ) => {
+        if (
+          visible === false ||
+          (typeof visible === 'function' && values && !!visible(values) === false)
+        ) {
+          return null;
+        }
+        if (isPro) {
+          proIndex = index;
+          formItemProps.hidden = !proOpened || Boolean(formItemProps.hidden);
+        }
+        const key = formItemProps.name || index.toString();
+        const useSpace = !!suffix;
+        (formItemProps.wrapperCol as any) = formItemProps.label ? undefined : 24;
 
-  const Content = useMemo(
-    () =>
-      ({ useSpace, suffix, extra, children, label, required, style }) =>
-        useSpace ? (
-          <Form.Item label={label} required={required} style={style} extra={extra}>
-            <Space>
-              {children}
-              {suffix}
-            </Space>
-          </Form.Item>
-        ) : (
-          children
-        ),
-    [],
-  );
+        const inner = (
+          <Content
+            key={key.toString()}
+            label={formItemProps.label}
+            extra={formItemProps.extra}
+            required={formItemProps.rules?.some(item => (item as any).required)}
+            suffix={(() => {
+              if ((suffix as SuffixDefineType)?.type) {
+                const {
+                  type,
+                  visible = true,
+                  props,
+                  ...formItemProps
+                } = suffix as SuffixDefineType;
+                return (
+                  ((typeof visible === 'boolean' && visible !== false) ||
+                    (typeof visible === 'function' && visible(values) !== false)) && (
+                    <FormItem
+                      type={type}
+                      formItemProps={formItemProps}
+                      useSpace={useSpace}
+                      props={props}
+                    />
+                  )
+                );
+              }
 
-  const body = useMemo(
-    () =>
-      content.map(
-        (
-          { visible = true, type = 'text', col, suffix, props, extraNames = [], ...formItemProps },
-          index,
-        ) => {
-          if (visible === false || (typeof visible === 'function' && !!visible(values) === false)) {
-            return null;
-          }
-          const key = formItemProps.name || index.toString();
-          const useSpace = !!suffix;
-          (formItemProps.wrapperCol as any) = formItemProps.label ? undefined : 24;
-
-          const inner = (
-            <Content
-              key={key.toString()}
-              label={formItemProps.label}
-              extra={formItemProps.extra}
-              required={formItemProps.rules?.some(item => (item as any).required)}
-              suffix={(() => {
-                if ((suffix as SuffixDefineType)?.type) {
-                  const {
-                    type,
-                    visible = true,
-                    props,
-                    ...formItemProps
-                  } = suffix as SuffixDefineType;
-                  return (
-                    ((typeof visible === 'boolean' && visible !== false) ||
-                      (typeof visible === 'function' && visible(values) !== false)) && (
-                      <FormItem
-                        type={type}
-                        formItemProps={formItemProps}
-                        useSpace={useSpace}
-                        props={props}
-                      />
-                    )
-                  );
-                }
-
-                return suffix;
-              })()}
-              useSpace={useSpace}
-              style={formItemProps.style}
-            >
+              return suffix;
+            })()}
+            useSpace={useSpace}
+            style={formItemProps.style}
+          >
+            <FormItem type={type} formItemProps={formItemProps} useSpace={useSpace} props={props} />
+            {extraNames.map(nameItem => (
               <FormItem
-                type={type}
-                formItemProps={formItemProps}
-                useSpace={useSpace}
-                props={props}
+                key={nameItem}
+                type="text"
+                formItemProps={{ name: nameItem, hidden: true }}
+                useSpace={false}
+                props={{}}
               />
-              {extraNames.map(nameItem => (
-                <FormItem
-                  key={nameItem}
-                  type="text"
-                  formItemProps={{ name: nameItem, hidden: true }}
-                  useSpace={false}
-                  props={{}}
-                />
-              ))}
-            </Content>
-          );
-
-          return useInline ? (
-            inner
-          ) : (
-            <Col key={key.toString()} span={col || 24}>
-              {inner}
-            </Col>
-          );
-        },
-      ),
-    // eslint-disable-next-line
-    [content, useInline, values],
-  );
+            ))}
+          </Content>
+        );
+
+        return useInline ? (
+          inner
+        ) : (
+          <Col key={key.toString()} span={col || 24}>
+            {inner}
+          </Col>
+        );
+      },
+    );
+    if (proIndex >= 0) {
+      conts.splice(
+        proIndex,
+        0,
+        <TextSwitch key="_pro" value={proOpened} onChange={v => setProOpened(v)} />,
+      );
+    }
+
+    return conts;
+  }, [content, useInline, values, proOpened]);
 
   const renderBody = useInline ? <>{body}</> : <Row gutter={20}>{body}</Row>;
   const targetResult = target ? createPortal(renderBody, target) : renderBody;
diff --git a/inlong-dashboard/src/metas/sinks/clickhouse.tsx b/inlong-dashboard/src/metas/sinks/clickhouse.tsx
index 69ad65a46..76623d32f 100644
--- a/inlong-dashboard/src/metas/sinks/clickhouse.tsx
+++ b/inlong-dashboard/src/metas/sinks/clickhouse.tsx
@@ -17,18 +17,11 @@
  * under the License.
  */
 
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
 import i18n from '@/i18n';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// ClickHouse targetType
 const clickhouseTargetTypes = [
   'String',
   'Int8',
@@ -44,227 +37,214 @@ const clickhouseTargetTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type: 'form' | 'col' = 'form',
-  { currentValues, inlongGroupId, isEdit, dataType } = {} as any,
-) => {
-  const fileds = [
-    {
-      name: 'dbName',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Clickhouse.DbName'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'tableName',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Clickhouse.TableName'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'enableCreateResource',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      name: 'username',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'password',
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      rules: [{ required: false }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const clickhouse: FieldItemType[] = [
+  {
+    name: 'dbName',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Clickhouse.DbName'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'tableName',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Clickhouse.TableName'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'enableCreateResource',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:clickhouse://127.0.0.1:8123',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      name: 'flushInterval',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Clickhouse.FlushInterval'),
-      initialValue: 1,
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-      suffix: i18n.t('meta.Sinks.Clickhouse.FlushIntervalUnit'),
-    },
-    {
-      name: 'flushRecord',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Clickhouse.FlushRecord'),
-      initialValue: 1000,
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-      suffix: i18n.t('meta.Sinks.Clickhouse.FlushRecordUnit'),
-    },
-    {
-      name: 'retryTime',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Clickhouse.RetryTimes'),
-      initialValue: 3,
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-      suffix: i18n.t('meta.Sinks.Clickhouse.RetryTimesUnit'),
-    },
-    {
-      name: 'isDistributed',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.Clickhouse.IsDistributed'),
-      initialValue: 0,
-      props: {
-        options: [
-          {
-            label: i18n.t('meta.Sinks.Clickhouse.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('meta.Sinks.Clickhouse.No'),
-            value: 0,
-          },
-        ],
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-    },
-    {
-      name: 'partitionStrategy',
-      type: 'select',
-      label: i18n.t('meta.Sinks.Clickhouse.PartitionStrategy'),
-      initialValue: 'BALANCE',
-      rules: [{ required: true }],
-      props: {
-        options: [
-          {
-            label: 'BALANCE',
-            value: 'BALANCE',
-          },
-          {
-            label: 'RANDOM',
-            value: 'RANDOM',
-          },
-          {
-            label: 'HASH',
-            value: 'HASH',
-          },
-        ],
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      visible: values => values.isDistributed,
-    },
-    {
-      name: 'partitionFields',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Clickhouse.PartitionFields'),
-      rules: [{ required: true }],
-      visible: values => values.isDistributed && values.partitionStrategy === 'HASH',
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'engine',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Clickhouse.Engine'),
-      initialValue: 'Log',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'orderBy',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Clickhouse.OrderBy'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'partitionBy',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Clickhouse.PartitionBy'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'primaryKey',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Clickhouse.PrimaryKey'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'sinkFieldList',
-      type: EditableTable,
-      props: {
-        size: 'small',
-        editing: ![110, 130].includes(currentValues?.status),
-        columns: getFieldListColumns(dataType, currentValues),
-      },
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    name: 'username',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'password',
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:clickhouse://127.0.0.1:8123',
+    }),
+  },
+  {
+    name: 'flushInterval',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Clickhouse.FlushInterval'),
+    initialValue: 1,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    rules: [{ required: true }],
+    suffix: i18n.t('meta.Sinks.Clickhouse.FlushIntervalUnit'),
+  },
+  {
+    name: 'flushRecord',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Clickhouse.FlushRecord'),
+    initialValue: 1000,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    rules: [{ required: true }],
+    suffix: i18n.t('meta.Sinks.Clickhouse.FlushRecordUnit'),
+  },
+  {
+    name: 'retryTime',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Clickhouse.RetryTimes'),
+    initialValue: 3,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    rules: [{ required: true }],
+    suffix: i18n.t('meta.Sinks.Clickhouse.RetryTimesUnit'),
+  },
+  {
+    name: 'isDistributed',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.Clickhouse.IsDistributed'),
+    initialValue: 0,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('meta.Sinks.Clickhouse.Yes'),
+          value: 1,
+        },
+        {
+          label: i18n.t('meta.Sinks.Clickhouse.No'),
+          value: 0,
+        },
+      ],
+    }),
+    rules: [{ required: true }],
+  },
+  {
+    name: 'partitionStrategy',
+    type: 'select',
+    label: i18n.t('meta.Sinks.Clickhouse.PartitionStrategy'),
+    initialValue: 'BALANCE',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: 'BALANCE',
+          value: 'BALANCE',
+        },
+        {
+          label: 'RANDOM',
+          value: 'RANDOM',
+        },
+        {
+          label: 'HASH',
+          value: 'HASH',
+        },
+      ],
+    }),
+    visible: values => values.isDistributed,
+  },
+  {
+    name: 'partitionFields',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Clickhouse.PartitionFields'),
+    rules: [{ required: true }],
+    visible: values => values.isDistributed && values.partitionStrategy === 'HASH',
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'engine',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Clickhouse.Engine'),
+    initialValue: 'Log',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'orderBy',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Clickhouse.OrderBy'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'partitionBy',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Clickhouse.PartitionBy'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'primaryKey',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Clickhouse.PrimaryKey'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -278,7 +258,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -287,7 +267,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       initialValue: clickhouseTargetTypes[0].value,
       type: 'select',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
         options: clickhouseTargetTypes,
       }),
       rules: [{ required: true }],
@@ -297,7 +277,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'defaultType',
       type: 'autocomplete',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
         options: ['DEFAULT', 'EPHEMERAL', 'MATERIALIZED', 'ALIAS'].map(item => ({
           label: item,
           value: item,
@@ -309,7 +289,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'defaultExpr',
       type: 'input',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       visible: (text, record) =>
         ['DEFAULT', 'EPHEMERAL', 'MATERIALIZED', 'ALIAS'].includes(record.defaultType as string),
@@ -318,30 +298,22 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       title: i18n.t('meta.Sinks.Clickhouse.CompressionCode'),
       dataIndex: 'compressionCode',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
       title: i18n.t('meta.Sinks.Clickhouse.TtlExpr'),
       dataIndex: 'ttlExpr',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
       title: `ClickHouse${i18n.t('meta.Sinks.Clickhouse.FieldDescription')}`,
       dataIndex: 'fieldComment',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const clickhouse = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/common/status.tsx b/inlong-dashboard/src/metas/sinks/common/status.tsx
new file mode 100644
index 000000000..6d836ecc2
--- /dev/null
+++ b/inlong-dashboard/src/metas/sinks/common/status.tsx
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+import React from 'react';
+import i18n from '@/i18n';
+import StatusTag, { StatusTagProps } from '@/components/StatusTag';
+import { ClockCircleFilled } from '@/components/Icons';
+
+type StatusProp = {
+  label: string;
+  value: string | number;
+  type: StatusTagProps['type'];
+  icon?: StatusTagProps['icon'];
+};
+
+export const statusList: StatusProp[] = [
+  {
+    label: i18n.t('pages.GroupDetail.Sink.Status.New'),
+    value: 100,
+    type: 'default',
+    icon: <ClockCircleFilled />,
+  },
+  {
+    label: i18n.t('pages.GroupDetail.Sink.Status.Pending'),
+    value: 110,
+    type: 'primary',
+  },
+  {
+    label: i18n.t('pages.GroupDetail.Sink.Status.Error'),
+    value: 120,
+    type: 'error',
+  },
+  {
+    label: i18n.t('pages.GroupDetail.Sink.Status.Success'),
+    value: 130,
+    type: 'success',
+  },
+];
+
+export const statusMap = statusList.reduce(
+  (acc, cur) => ({
+    ...acc,
+    [cur.value]: cur,
+  }),
+  {},
+);
+
+export const genStatusTag = (value: StatusProp['value']) => {
+  const item = statusMap[value] || {};
+
+  return <StatusTag type={item.type || 'default'} title={item.label || value} icon={item.icon} />;
+};
diff --git a/inlong-dashboard/src/metas/sinks/es.tsx b/inlong-dashboard/src/metas/sinks/es.tsx
index 4e8c4baa8..bb104a2d4 100644
--- a/inlong-dashboard/src/metas/sinks/es.tsx
+++ b/inlong-dashboard/src/metas/sinks/es.tsx
@@ -17,15 +17,9 @@
  * under the License.
  */
 
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
 import i18n from '@/i18n';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
 const esTypes = [
@@ -46,138 +40,126 @@ const esTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type: 'form' | 'col' = 'form',
-  { currentValues, inlongGroupId, isEdit, dataType } = {} as any,
-) => {
-  const fileds = [
-    {
-      name: 'indexName',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Es.IndexName'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'enableCreateResource',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      name: 'username',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'password',
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const es: FieldItemType[] = [
+  {
+    name: 'indexName',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Es.IndexName'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'enableCreateResource',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Es.Host'),
-      name: 'host',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'port',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Es.Port'),
-      initialValue: 9200,
-      props: {
-        min: 1,
-        max: 65535,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-    },
-    {
-      name: 'flushInterval',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Es.FlushInterval'),
-      initialValue: 1,
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-      suffix: i18n.t('meta.Sinks.Es.FlushIntervalUnit'),
-    },
-    {
-      name: 'flushRecord',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Es.FlushRecord'),
-      initialValue: 1000,
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-      suffix: i18n.t('meta.Sinks.Es.FlushRecordUnit'),
-    },
-    {
-      name: 'retryTime',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Es.RetryTimes'),
-      initialValue: 3,
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-      suffix: i18n.t('meta.Sinks.Es.RetryTimesUnit'),
-    },
-    {
-      name: 'sinkFieldList',
-      type: EditableTable,
-      props: {
-        size: 'small',
-        editing: ![110, 130].includes(currentValues?.status),
-        columns: getFieldListColumns(dataType, currentValues),
-      },
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    name: 'username',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'password',
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Es.Host'),
+    name: 'host',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'port',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Es.Port'),
+    initialValue: 9200,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+      max: 65535,
+    }),
+    rules: [{ required: true }],
+  },
+  {
+    name: 'flushInterval',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Es.FlushInterval'),
+    initialValue: 1,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    rules: [{ required: true }],
+    suffix: i18n.t('meta.Sinks.Es.FlushIntervalUnit'),
+  },
+  {
+    name: 'flushRecord',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Es.FlushRecord'),
+    initialValue: 1000,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    rules: [{ required: true }],
+    suffix: i18n.t('meta.Sinks.Es.FlushRecordUnit'),
+  },
+  {
+    name: 'retryTime',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Es.RetryTimes'),
+    initialValue: 3,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    rules: [{ required: true }],
+    suffix: i18n.t('meta.Sinks.Es.RetryTimesUnit'),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -191,7 +173,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -200,7 +182,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       initialValue: esTypes[0].value,
       type: 'select',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
         options: esTypes,
       }),
       rules: [{ required: true }],
@@ -210,7 +192,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'analyzer',
       type: 'input',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       visible: (text, record) => record.fieldType === 'text',
     },
@@ -218,7 +200,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       title: 'SearchAnalyzer',
       dataIndex: 'searchAnalyzer',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       visible: (text, record) => record.fieldType === 'text',
     },
@@ -226,7 +208,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       title: i18n.t('meta.Sinks.Es.DateFormat'),
       dataIndex: 'format',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       visible: (text, record) => record.fieldType === 'date',
     },
@@ -234,7 +216,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       title: 'ScalingFactor',
       dataIndex: 'scalingFactor',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       visible: (text, record) => record.fieldType === 'scaled_float',
     },
@@ -242,16 +224,8 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       title: `ES ${i18n.t('meta.Sinks.Es.FieldDescription')}`,
       dataIndex: 'fieldComment',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const es = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/greenplum.tsx b/inlong-dashboard/src/metas/sinks/greenplum.tsx
index e0f988a93..aff09821c 100644
--- a/inlong-dashboard/src/metas/sinks/greenplum.tsx
+++ b/inlong-dashboard/src/metas/sinks/greenplum.tsx
@@ -15,19 +15,11 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// greenplumFieldTypes
 const greenplumFieldTypes = [
   'SMALLINT',
   'INT2',
@@ -58,103 +50,89 @@ const greenplumFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:postgresql://127.0.0.1:5432/write',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Greenplum.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Greenplum.PrimaryKey'),
-      name: 'primaryKey',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      name: 'username',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      name: 'password',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const greenplum: FieldItemType[] = [
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:postgresql://127.0.0.1:5432/write',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Greenplum.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Greenplum.PrimaryKey'),
+    name: 'primaryKey',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    name: 'username',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    name: 'password',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      canDelete: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -169,7 +147,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -179,7 +157,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       type: 'select',
       props: (text, record, idx, isNew) => ({
         options: greenplumFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -220,13 +198,5 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'fieldComment',
       initialValue: '',
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const greenplum = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/hbase.tsx b/inlong-dashboard/src/metas/sinks/hbase.tsx
index 6d46d2db0..e1c39d4c8 100644
--- a/inlong-dashboard/src/metas/sinks/hbase.tsx
+++ b/inlong-dashboard/src/metas/sinks/hbase.tsx
@@ -15,19 +15,11 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// hbaseFieldTypes
 const hbaseFieldTypes = [
   'int',
   'short',
@@ -43,119 +35,105 @@ const hbaseFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.HBase.Namespace'),
-      name: 'namespace',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.HBase.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.HBase.RowKey'),
-      name: 'rowKey',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.HBase.ZkQuorum'),
-      name: 'zkQuorum',
-      rules: [{ required: true }],
-      props: {
-        placeholder: '127.0.0.1:2181,127.0.0.2:2181',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.HBase.ZkNodeParent'),
-      name: 'zkNodeParent',
-      rules: [{ required: true }],
-      props: {
-        placeholder: '/hbase',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.HBase.BufferFlushMaxSize'),
-      name: 'bufferFlushMaxSize',
-      initialValue: 2,
-      rules: [{ required: true }],
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      suffix: 'mb',
-      _inTable: true,
-    },
-    {
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.HBase.BufferFlushMaxRows'),
-      name: 'bufferFlushMaxRows',
-      initialValue: 1000,
-      rules: [{ required: true }],
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.HBase.BufferFlushInterval'),
-      name: 'bufferFlushInterval',
-      initialValue: 1,
-      rules: [{ required: true }],
-      props: {
-        min: 1,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      suffix: i18n.t('meta.Sinks.HBase.FlushIntervalUnit'),
-      _inTable: true,
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-  ];
+export const hbase: FieldItemType[] = [
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.HBase.Namespace'),
+    name: 'namespace',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.HBase.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.HBase.RowKey'),
+    name: 'rowKey',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.HBase.ZkQuorum'),
+    name: 'zkQuorum',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: '127.0.0.1:2181,127.0.0.2:2181',
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.HBase.ZkNodeParent'),
+    name: 'zkNodeParent',
+    initialValue: '/hbase',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.HBase.BufferFlushMaxSize'),
+    name: 'bufferFlushMaxSize',
+    initialValue: 2,
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    suffix: 'mb',
+  },
+  {
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.HBase.BufferFlushMaxRows'),
+    name: 'bufferFlushMaxRows',
+    initialValue: 1000,
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+  },
+  {
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.HBase.BufferFlushInterval'),
+    name: 'bufferFlushInterval',
+    initialValue: 1,
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+    }),
+    suffix: i18n.t('meta.Sinks.HBase.FlushIntervalUnit'),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      canDelete: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
-
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -170,7 +148,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -180,7 +158,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       type: 'select',
       props: (text, record, idx, isNew) => ({
         options: hbaseFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -189,7 +167,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'cfName',
       type: 'input',
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -199,17 +177,9 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       type: 'inputnumber',
       props: (text, record, idx, isNew) => ({
         min: 1,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const hbase = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/hive.tsx b/inlong-dashboard/src/metas/sinks/hive.tsx
index bf28d7da4..2065ff748 100644
--- a/inlong-dashboard/src/metas/sinks/hive.tsx
+++ b/inlong-dashboard/src/metas/sinks/hive.tsx
@@ -17,21 +17,11 @@
  * under the License.
  */
 
-import React from 'react';
-// import { Button, message } from 'antd';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
-// import request from '@/utils/request';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// hiveFieldTypes
 const hiveFieldTypes = [
   'string',
   'varchar',
@@ -54,295 +44,260 @@ const hiveFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Hive.DbName'),
-      name: 'dbName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Hive.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      name: 'username',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      name: 'password',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const hive: FieldItemType[] = [
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Hive.DbName'),
+    name: 'dbName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Hive.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:hive2://127.0.0.1:10000',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-      /*suffix: (
-        <Button
-          onClick={async () => {
-            const values = await form.validateFields(['username', 'password', 'jdbcUrl']);
-            const res = await request({
-              url: '/sink/query/testConnection',
-              method: 'POST',
-              data: values,
-            });
-            res
-              ? message.success(
-                  i18n.t('meta.Sinks.Hive.ConnectionSucceeded'),
-                )
-              : message.error(
-                  i18n.t('meta.Sinks.Hive.ConnectionFailed'),
-                );
-          }}
-        >
-          {i18n.t('meta.Sinks.Hive.ConnectionTest')}
-        </Button>
-      ),*/
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Hive.DataPath'),
-      name: 'dataPath',
-      rules: [{ required: true }],
-      tooltip: i18n.t('meta.Sinks.DataPathHelp'),
-      props: {
-        placeholder: 'hdfs://127.0.0.1:9000/user/hive/warehouse/default',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Hive.ConfDir'),
-      name: 'hiveConfDir',
-      rules: [{ required: true }],
-      tooltip: i18n.t('meta.Sinks.Hive.ConfDirHelp'),
-      props: {
-        placeholder: '/usr/hive/conf',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'fileFormat',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.Hive.FileFormat'),
-      initialValue: 'TextFile',
-      rules: [{ required: true }],
-      props: {
-        options: [
-          {
-            label: 'TextFile',
-            value: 'TextFile',
-          },
-          {
-            label: 'SequenceFile',
-            value: 'SequenceFile',
-          },
-          // {
-          //   label: 'RcFile',
-          //   value: 'RcFile',
-          // },
-          {
-            label: 'OrcFile',
-            value: 'OrcFile',
-          },
-          {
-            label: 'Parquet',
-            value: 'Parquet',
-          },
-          {
-            label: 'Avro',
-            value: 'Avro',
-          },
-        ],
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'dataEncoding',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.Hive.DataEncoding'),
-      initialValue: 'UTF-8',
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: 'UTF-8',
-            value: 'UTF-8',
-          },
-          {
-            label: 'GBK',
-            value: 'GBK',
-          },
-        ],
-      },
-      rules: [{ required: true }],
-    },
-    {
-      name: 'dataSeparator',
-      type: 'select',
-      label: i18n.t('meta.Sinks.Hive.DataSeparator'),
-      initialValue: '124',
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        dropdownMatchSelectWidth: false,
-        options: [
-          {
-            label: i18n.t('meta.Sinks.Hive.DataSeparator.VerticalLine'),
-            value: '124',
-          },
-          {
-            label: i18n.t('meta.Sinks.Hive.DataSeparator.Comma'),
-            value: '44',
-          },
-          {
-            label: i18n.t('meta.Sinks.Hive.DataSeparator.DoubleQuotes'),
-            value: '34',
-          },
-          {
-            label: i18n.t('meta.Sinks.Hive.DataSeparator.Asterisk'),
-            value: '42',
-          },
-          {
-            label: i18n.t('meta.Sinks.Hive.DataSeparator.Space'),
-            value: '32',
-          },
-          {
-            label: i18n.t('meta.Sinks.Hive.DataSeparator.Semicolon'),
-            value: '59',
-          },
-        ],
-        useInput: true,
-        useInputProps: {
-          placeholder: 'ASCII',
-          disabled: isEdit && [110, 130].includes(currentValues?.status),
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
         },
-        style: { width: 100 },
-      },
-      rules: [
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    name: 'username',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    name: 'password',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:hive2://127.0.0.1:10000',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Hive.DataPath'),
+    name: 'dataPath',
+    rules: [{ required: true }],
+    tooltip: i18n.t('meta.Sinks.DataPathHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'hdfs://127.0.0.1:9000/user/hive/warehouse/default',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Hive.ConfDir'),
+    name: 'hiveConfDir',
+    rules: [{ required: true }],
+    tooltip: i18n.t('meta.Sinks.Hive.ConfDirHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: '/usr/hive/conf',
+    }),
+  },
+  {
+    name: 'fileFormat',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.Hive.FileFormat'),
+    initialValue: 'TextFile',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
         {
-          required: true,
-          type: 'number',
-          transform: val => +val,
-          min: 0,
-          max: 127,
-        } as any,
+          label: 'TextFile',
+          value: 'TextFile',
+        },
+        {
+          label: 'SequenceFile',
+          value: 'SequenceFile',
+        },
+        // {
+        //   label: 'RcFile',
+        //   value: 'RcFile',
+        // },
+        {
+          label: 'OrcFile',
+          value: 'OrcFile',
+        },
+        {
+          label: 'Parquet',
+          value: 'Parquet',
+        },
+        {
+          label: 'Avro',
+          value: 'Avro',
+        },
       ],
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-    {
-      name: 'partitionFieldList',
-      label: i18n.t('meta.Sinks.Hive.PartitionFieldList'),
-      type: EditableTable,
-      tooltip: i18n.t('meta.Sinks.Hive.PartitionFieldListHelp'),
-      props: {
-        size: 'small',
-        required: false,
-        columns: [
-          {
-            title: i18n.t('meta.Sinks.Hive.FieldName'),
-            dataIndex: 'fieldName',
-            rules: [{ required: true }],
-          },
-          {
-            title: i18n.t('meta.Sinks.Hive.FieldType'),
-            dataIndex: 'fieldType',
-            type: 'select',
-            initialValue: 'string',
-            props: {
-              options: ['string', 'timestamp'].map(item => ({
-                label: item,
-                value: item,
-              })),
-            },
+    }),
+  },
+  {
+    name: 'dataEncoding',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.Hive.DataEncoding'),
+    initialValue: 'UTF-8',
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: 'UTF-8',
+          value: 'UTF-8',
+        },
+        {
+          label: 'GBK',
+          value: 'GBK',
+        },
+      ],
+    }),
+    rules: [{ required: true }],
+  },
+  {
+    name: 'dataSeparator',
+    type: 'select',
+    label: i18n.t('meta.Sinks.Hive.DataSeparator'),
+    initialValue: '124',
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      dropdownMatchSelectWidth: false,
+      options: [
+        {
+          label: i18n.t('meta.Sinks.Hive.DataSeparator.VerticalLine'),
+          value: '124',
+        },
+        {
+          label: i18n.t('meta.Sinks.Hive.DataSeparator.Comma'),
+          value: '44',
+        },
+        {
+          label: i18n.t('meta.Sinks.Hive.DataSeparator.DoubleQuotes'),
+          value: '34',
+        },
+        {
+          label: i18n.t('meta.Sinks.Hive.DataSeparator.Asterisk'),
+          value: '42',
+        },
+        {
+          label: i18n.t('meta.Sinks.Hive.DataSeparator.Space'),
+          value: '32',
+        },
+        {
+          label: i18n.t('meta.Sinks.Hive.DataSeparator.Semicolon'),
+          value: '59',
+        },
+      ],
+      useInput: true,
+      useInputProps: {
+        placeholder: 'ASCII',
+        disabled: [110, 130].includes(values?.status),
+      },
+      style: { width: 100 },
+    }),
+    rules: [
+      {
+        required: true,
+        type: 'number',
+        transform: val => +val,
+        min: 0,
+        max: 127,
+      } as any,
+    ],
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      columns: getFieldListColumns(values),
+      canDelete: ![110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'partitionFieldList',
+    label: i18n.t('meta.Sinks.Hive.PartitionFieldList'),
+    type: EditableTable,
+    tooltip: i18n.t('meta.Sinks.Hive.PartitionFieldListHelp'),
+    props: {
+      size: 'small',
+      required: false,
+      columns: [
+        {
+          title: i18n.t('meta.Sinks.Hive.FieldName'),
+          dataIndex: 'fieldName',
+          rules: [{ required: true }],
+        },
+        {
+          title: i18n.t('meta.Sinks.Hive.FieldType'),
+          dataIndex: 'fieldType',
+          type: 'select',
+          initialValue: 'string',
+          props: {
+            options: ['string', 'timestamp'].map(item => ({
+              label: item,
+              value: item,
+            })),
           },
-          {
-            title: i18n.t('meta.Sinks.Hive.FieldFormat'),
-            dataIndex: 'fieldFormat',
-            type: 'autocomplete',
-            props: {
-              options: ['MICROSECONDS', 'MILLISECONDS', 'SECONDS', 'SQL', 'ISO_8601'].map(item => ({
-                label: item,
-                value: item,
-              })),
-            },
-            rules: [{ required: true }],
-            visible: (text, record) => record.fieldType === 'timestamp',
+        },
+        {
+          title: i18n.t('meta.Sinks.Hive.FieldFormat'),
+          dataIndex: 'fieldFormat',
+          type: 'autocomplete',
+          props: {
+            options: ['MICROSECONDS', 'MILLISECONDS', 'SECONDS', 'SQL', 'ISO_8601'].map(item => ({
+              label: item,
+              value: item,
+            })),
           },
-        ],
-      },
+          rules: [{ required: true }],
+          visible: (text, record) => record.fieldType === 'timestamp',
+        },
+      ],
     },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -357,7 +312,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -367,7 +322,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       type: 'select',
       props: (text, record, idx, isNew) => ({
         options: hiveFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -408,13 +363,5 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'fieldComment',
       initialValue: '',
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const hive = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/iceberg.tsx b/inlong-dashboard/src/metas/sinks/iceberg.tsx
index a9259fdf9..458dcb644 100644
--- a/inlong-dashboard/src/metas/sinks/iceberg.tsx
+++ b/inlong-dashboard/src/metas/sinks/iceberg.tsx
@@ -17,20 +17,10 @@
  * under the License.
  */
 
-import React from 'react';
 import i18n from '@/i18n';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
+import type { FieldItemType } from '@/metas/common';
 import EditableTable from '@/components/EditableTable';
-import { excludeObject } from '@/utils';
-import TextSwitch from '@/components/TextSwitch';
 import { sourceFields } from './common/sourceFields';
-// import { Button, message } from 'antd';
-// import request from '@/utils/request';
 
 const icebergFieldTypes = [
   'string',
@@ -108,182 +98,149 @@ const matchPartitionStrategies = fieldType => {
   return data.filter(item => !item.disabled);
 };
 
-const getForm: GetStorageFormFieldsType = (
-  type: 'form' | 'col' = 'form',
-  { currentValues, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      name: 'dbName',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Iceberg.DbName'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'tableName',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Iceberg.TableName'),
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: 'Catalog URI',
-      name: 'catalogUri',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'thrift://127.0.0.1:9083',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-      /*suffix: (
-        <Button
-          onClick={async () => {
-            const values = await form.validateFields(['username', 'password', 'jdbcUrl']);
-            const res = await request({
-              url: '/sink/query/testConnection',
-              method: 'POST',
-              data: values,
-            });
-            res
-              ? message.success(
-                  i18n.t('meta.Sinks.Hive.ConnectionSucceeded'),
-                )
-              : message.error(
-                  i18n.t('meta.Sinks.Hive.ConnectionFailed'),
-                );
-          }}
-        >
-          {i18n.t('meta.Sinks.Hive.ConnectionTest')}
-        </Button>
-      ),*/
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Iceberg.Warehouse'),
-      name: 'warehouse',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'hdfs://127.0.0.1:9000/user/iceberg/warehouse',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-    {
-      name: 'fileFormat',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.Iceberg.FileFormat'),
-      initialValue: 'Parquet',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: 'Parquet',
-            value: 'Parquet',
-          },
-          {
-            label: 'Orc',
-            value: 'Orc',
-          },
-          {
-            label: 'Avro',
-            value: 'Avro',
-          },
-        ],
-      },
-    },
-    {
-      name: 'extList',
-      label: i18n.t('meta.Sinks.Iceberg.ExtList'),
-      type: EditableTable,
-      props: {
-        size: 'small',
-        editing: !isEdit,
-        columns: [
-          {
-            title: 'Key',
-            dataIndex: 'keyName',
-            props: (text, record, idx, isNew) => ({
-              disabled: [110, 130].includes(currentValues?.status) && !isNew,
-            }),
-          },
-          {
-            title: 'Value',
-            dataIndex: 'keyValue',
-            props: (text, record, idx, isNew) => ({
-              disabled: [110, 130].includes(currentValues?.status) && !isNew,
-            }),
-          },
-        ],
-      },
-      initialValue: [],
-    },
-    { name: '_showHigher', type: <TextSwitch />, initialValue: false },
-    {
-      name: 'dataConsistency',
-      type: 'select',
-      label: i18n.t('meta.Sinks.Iceberg.DataConsistency'),
-      initialValue: 'EXACTLY_ONCE',
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: 'EXACTLY_ONCE',
-            value: 'EXACTLY_ONCE',
+export const iceberg: FieldItemType[] = [
+  {
+    name: 'dbName',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Iceberg.DbName'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'tableName',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Iceberg.TableName'),
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
+        },
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: 'Catalog URI',
+    name: 'catalogUri',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'thrift://127.0.0.1:9083',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Iceberg.Warehouse'),
+    name: 'warehouse',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'hdfs://127.0.0.1:9000/user/iceberg/warehouse',
+    }),
+  },
+  {
+    name: 'fileFormat',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.Iceberg.FileFormat'),
+    initialValue: 'Parquet',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: 'Parquet',
+          value: 'Parquet',
+        },
+        {
+          label: 'Orc',
+          value: 'Orc',
+        },
+        {
+          label: 'Avro',
+          value: 'Avro',
+        },
+      ],
+    }),
+  },
+  {
+    name: 'extList',
+    label: i18n.t('meta.Sinks.Iceberg.ExtList'),
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      columns: [
+        {
+          title: 'Key',
+          dataIndex: 'keyName',
+          props: {
+            disabled: [110, 130].includes(values?.status),
           },
-          {
-            label: 'AT_LEAST_ONCE',
-            value: 'AT_LEAST_ONCE',
+        },
+        {
+          title: 'Value',
+          dataIndex: 'keyValue',
+          props: {
+            disabled: [110, 130].includes(values?.status),
           },
-        ],
-      },
-      hidden: !currentValues?._showHigher,
-    },
-    {
-      name: 'sinkFieldList',
-      type: EditableTable,
-      props: {
-        size: 'small',
-        editing: ![110, 130].includes(currentValues?.status),
-        columns: getFieldListColumns(dataType, currentValues),
-      },
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        },
+      ],
+    }),
+    initialValue: [],
+  },
+  {
+    name: 'dataConsistency',
+    type: 'select',
+    label: i18n.t('meta.Sinks.Iceberg.DataConsistency'),
+    initialValue: 'EXACTLY_ONCE',
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: 'EXACTLY_ONCE',
+          value: 'EXACTLY_ONCE',
+        },
+        {
+          label: 'AT_LEAST_ONCE',
+          value: 'AT_LEAST_ONCE',
+        },
+      ],
+    }),
+    isPro: true,
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -298,7 +255,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -318,7 +275,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
             };
           }
         },
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -370,11 +327,3 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
     },
   ];
 };
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const iceberg = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
-};
diff --git a/inlong-dashboard/src/metas/sinks/index.ts b/inlong-dashboard/src/metas/sinks/index.ts
index 4e3998e6e..b1c7456c0 100644
--- a/inlong-dashboard/src/metas/sinks/index.ts
+++ b/inlong-dashboard/src/metas/sinks/index.ts
@@ -17,8 +17,11 @@
  * under the License.
  */
 
-import type { GetStorageFormFieldsType, GetStorageColumnsType } from '@/utils/metaData';
-import type { ColumnsType } from 'antd/es/table';
+import i18n from '@/i18n';
+import type { FieldItemType } from '@/metas/common';
+import { genFields, genForm, genTable } from '@/metas/common';
+import { statusList, genStatusTag } from './common/status';
+
 import { hive } from './hive';
 import { clickhouse } from './clickhouse';
 import { kafka } from './kafka';
@@ -32,80 +35,140 @@ import { sqlServer } from './sqlServer';
 import { tdsqlPostgreSQL } from './tdsqlPostgreSql';
 import { hbase } from './hbase';
 
-export interface SinkType {
-  label: string;
-  value: string;
-  // Generate form configuration for single data
-  getForm: GetStorageFormFieldsType;
-  // Generate table display configuration
-  tableColumns: ColumnsType;
-  // Detailed mapping data field configuration for this type of flow
-  getFieldListColumns?: GetStorageColumnsType;
-  // Custom convert interface data to front-end data format
-  toFormValues?: (values: unknown) => unknown;
-  // Custom convert front-end data to interface data format
-  toSubmitValues?: (values: unknown) => unknown;
-}
-
-export const Sinks: SinkType[] = [
+const allSinks = [
   {
     label: 'Hive',
     value: 'HIVE',
-    ...hive,
+    fields: hive,
   },
   {
     label: 'Iceberg',
     value: 'ICEBERG',
-    ...iceberg,
+    fields: iceberg,
   },
   {
     label: 'ClickHouse',
     value: 'CLICKHOUSE',
-    ...clickhouse,
+    fields: clickhouse,
   },
   {
     label: 'Kafka',
     value: 'KAFKA',
-    ...kafka,
+    fields: kafka,
   },
   {
     label: 'Elasticsearch',
     value: 'ELASTICSEARCH',
-    ...es,
+    fields: es,
   },
   {
     label: 'Greenplum',
     value: 'GREENPLUM',
-    ...greenplum,
+    fields: greenplum,
   },
   {
     label: 'HBase',
     value: 'HBASE',
-    ...hbase,
+    fields: hbase,
   },
   {
     label: 'MySQL',
     value: 'MYSQL',
-    ...mysql,
+    fields: mysql,
   },
   {
     label: 'Oracle',
     value: 'ORACLE',
-    ...oracle,
+    fields: oracle,
   },
   {
     label: 'PostgreSQL',
     value: 'POSTGRES',
-    ...postgreSql,
+    fields: postgreSql,
   },
   {
     label: 'SQLServer',
     value: 'SQLSERVER',
-    ...sqlServer,
+    fields: sqlServer,
   },
   {
     label: 'TDSQLPostgreSQL',
     value: 'TDSQLPOSTGRESQL',
-    ...tdsqlPostgreSQL,
+    fields: tdsqlPostgreSQL,
+  },
+].sort((a, b) => {
+  const a1 = a.label.toUpperCase();
+  const a2 = b.label.toUpperCase();
+  if (a1 < a2) return -1;
+  if (a1 > a2) return 1;
+  return 0;
+});
+
+const defaultCommonFields: FieldItemType[] = [
+  {
+    name: 'sinkName',
+    type: 'input',
+    label: i18n.t('meta.Sinks.SinkName'),
+    rules: [
+      { required: true },
+      {
+        pattern: /^[a-zA-Z][a-zA-Z0-9_-]*$/,
+        message: i18n.t('meta.Sinks.SinkNameRule'),
+      },
+    ],
+    props: values => ({
+      disabled: !!values.id,
+      maxLength: 128,
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'sinkType',
+    type: 'select',
+    label: i18n.t('meta.Sinks.SinkType'),
+    rules: [{ required: true }],
+    initialValue: allSinks[0].value,
+    props: values => ({
+      dropdownMatchSelectWidth: false,
+      disabled: !!values.id,
+      options: allSinks,
+    }),
+  },
+  {
+    name: 'description',
+    type: 'textarea',
+    label: i18n.t('meta.Sinks.Description'),
+    props: {
+      showCount: true,
+      maxLength: 300,
+    },
+  },
+  {
+    name: 'status',
+    type: 'select',
+    label: i18n.t('basic.Status'),
+    props: {
+      allowClear: true,
+      options: statusList,
+      dropdownMatchSelectWidth: false,
+    },
+    visible: false,
+    _renderTable: {
+      render: text => genStatusTag(text),
+    },
   },
 ];
+
+export const sinks = allSinks.map(item => {
+  const itemFields = defaultCommonFields.concat(item.fields);
+  const fields = genFields(itemFields);
+
+  return {
+    ...item,
+    fields,
+    form: genForm(fields),
+    table: genTable(fields),
+    toFormValues: null,
+    toSubmitValues: null,
+  };
+});
diff --git a/inlong-dashboard/src/metas/sinks/kafka.tsx b/inlong-dashboard/src/metas/sinks/kafka.tsx
index 4d270fb57..cad277ce5 100644
--- a/inlong-dashboard/src/metas/sinks/kafka.tsx
+++ b/inlong-dashboard/src/metas/sinks/kafka.tsx
@@ -17,108 +17,90 @@
  * under the License.
  */
 
-import { getColsFromFields, GetStorageFormFieldsType } from '@/utils/metaData';
 import i18n from '@/i18n';
-import { ColumnsType } from 'antd/es/table';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
 
-const getForm: GetStorageFormFieldsType = (
-  type: 'form' | 'col' = 'form',
-  { currentValues, isEdit } = {} as any,
-) => {
-  const fileds = [
-    {
-      name: 'bootstrapServers',
-      type: 'input',
-      label: i18n.t('meta.Sinks.Kafka.Server'),
-      rules: [{ required: true }],
-      initialValue: '127.0.0.1:9092',
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'topicName',
-      type: 'input',
-      label: 'Topic',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'serializationType',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.Kafka.SerializationType'),
-      initialValue: 'JSON',
-      rules: [{ required: true }],
-      props: {
-        options: [
-          {
-            label: 'JSON',
-            value: 'JSON',
-          },
-          {
-            label: 'CANAL',
-            value: 'CANAL',
-          },
-          {
-            label: 'AVRO',
-            value: 'AVRO',
-          },
-        ],
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      name: 'partitionNum',
-      type: 'inputnumber',
-      label: i18n.t('meta.Sinks.Kafka.PartitionNum'),
-      initialValue: 3,
-      props: {
-        min: 1,
-        max: 30,
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      rules: [{ required: true }],
-    },
-    {
-      name: 'autoOffsetReset',
-      type: 'radio',
-      label: i18n.t('meta.Sinks.Kafka.AutoOffsetReset'),
-      initialValue: 'earliest',
-      rules: [{ required: true }],
-      props: {
-        options: [
-          {
-            label: 'earliest',
-            value: 'earliest',
-          },
-          {
-            label: 'latest',
-            value: 'latest',
-          },
-          {
-            label: 'none',
-            value: 'none',
-          },
-        ],
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const kafka = {
-  getForm,
-  tableColumns,
-};
+export const kafka: FieldItemType[] = [
+  {
+    name: 'bootstrapServers',
+    type: 'input',
+    label: i18n.t('meta.Sinks.Kafka.Server'),
+    rules: [{ required: true }],
+    initialValue: '127.0.0.1:9092',
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'topicName',
+    type: 'input',
+    label: 'Topic',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'serializationType',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.Kafka.SerializationType'),
+    initialValue: 'JSON',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: 'JSON',
+          value: 'JSON',
+        },
+        {
+          label: 'CANAL',
+          value: 'CANAL',
+        },
+        {
+          label: 'AVRO',
+          value: 'AVRO',
+        },
+      ],
+    }),
+    _renderTable: true,
+  },
+  {
+    name: 'partitionNum',
+    type: 'inputnumber',
+    label: i18n.t('meta.Sinks.Kafka.PartitionNum'),
+    initialValue: 3,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      min: 1,
+      max: 30,
+    }),
+    rules: [{ required: true }],
+  },
+  {
+    name: 'autoOffsetReset',
+    type: 'radio',
+    label: i18n.t('meta.Sinks.Kafka.AutoOffsetReset'),
+    initialValue: 'earliest',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: 'earliest',
+          value: 'earliest',
+        },
+        {
+          label: 'latest',
+          value: 'latest',
+        },
+        {
+          label: 'none',
+          value: 'none',
+        },
+      ],
+    }),
+  },
+];
diff --git a/inlong-dashboard/src/metas/sinks/mysql.tsx b/inlong-dashboard/src/metas/sinks/mysql.tsx
index b04c19837..bef4d7220 100644
--- a/inlong-dashboard/src/metas/sinks/mysql.tsx
+++ b/inlong-dashboard/src/metas/sinks/mysql.tsx
@@ -15,19 +15,11 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// mysqlFieldTypes
 const mysqlFieldTypes = [
   'TINYINT',
   'SMALLINT',
@@ -54,103 +46,88 @@ const mysqlFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:mysql://127.0.0.1:3306/write',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.MySQL.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Greenplum.PrimaryKey'),
-      name: 'primaryKey',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      name: 'username',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      name: 'password',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const mysql: FieldItemType[] = [
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:mysql://127.0.0.1:3306/write',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.MySQL.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Greenplum.PrimaryKey'),
+    name: 'primaryKey',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    name: 'username',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    name: 'password',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -165,7 +142,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -175,7 +152,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       type: 'select',
       props: (text, record, idx, isNew) => ({
         options: mysqlFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -216,13 +193,5 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'fieldComment',
       initialValue: '',
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const mysql = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/oracle.tsx b/inlong-dashboard/src/metas/sinks/oracle.tsx
index e154e0178..05e9ec4ce 100644
--- a/inlong-dashboard/src/metas/sinks/oracle.tsx
+++ b/inlong-dashboard/src/metas/sinks/oracle.tsx
@@ -15,19 +15,11 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-//  oracleFieldTypes
 const oracleFieldTypes = [
   'BINARY_FLOAT',
   'BINARY_DOUBLE',
@@ -53,103 +45,89 @@ const oracleFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:oracle:thin://127.0.0.1:1521/db_name',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Oracle.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Oracle.PrimaryKey'),
-      name: 'primaryKey',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      name: 'username',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      name: 'password',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const oracle: FieldItemType[] = [
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:oracle:thin://127.0.0.1:1521/db_name',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Oracle.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Oracle.PrimaryKey'),
+    name: 'primaryKey',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    name: 'username',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    name: 'password',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -164,7 +142,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -174,7 +152,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       type: 'select',
       props: (text, record, idx, isNew) => ({
         options: oracleFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -215,13 +193,5 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'fieldComment',
       initialValue: '',
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const oracle = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/postgreSql.tsx b/inlong-dashboard/src/metas/sinks/postgreSql.tsx
index b1d4edb19..ca27d07a0 100644
--- a/inlong-dashboard/src/metas/sinks/postgreSql.tsx
+++ b/inlong-dashboard/src/metas/sinks/postgreSql.tsx
@@ -15,20 +15,12 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// postgreSQLFieldTypes
-const postgreSQLFieldTypes = [
+const postgreSqlFieldTypes = [
   'SMALLINT',
   'INT2',
   'SMALLSERIAL',
@@ -57,113 +49,98 @@ const postgreSQLFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:postgresql://127.0.0.1:5432/db_name',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.PostgreSQL.DbName'),
-      name: 'dbName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.PostgreSQL.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.PostgreSQL.PrimaryKey'),
-      name: 'primaryKey',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      name: 'username',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      name: 'password',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const postgreSql: FieldItemType[] = [
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:postgresql://127.0.0.1:5432/db_name',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.PostgreSQL.DbName'),
+    name: 'dbName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.PostgreSQL.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.PostgreSQL.PrimaryKey'),
+    name: 'primaryKey',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    name: 'username',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    name: 'password',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -178,17 +155,17 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
       title: `POSTGRESQL${i18n.t('meta.Sinks.PostgreSQL.FieldType')}`,
       dataIndex: 'fieldType',
-      initialValue: postgreSQLFieldTypes[0].value,
+      initialValue: postgreSqlFieldTypes[0].value,
       type: 'select',
       props: (text, record, idx, isNew) => ({
-        options: postgreSQLFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        options: postgreSqlFieldTypes,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -229,13 +206,5 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'fieldComment',
       initialValue: '',
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const postgreSql = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/sqlServer.tsx b/inlong-dashboard/src/metas/sinks/sqlServer.tsx
index 3af312547..3a7c1fe18 100644
--- a/inlong-dashboard/src/metas/sinks/sqlServer.tsx
+++ b/inlong-dashboard/src/metas/sinks/sqlServer.tsx
@@ -15,19 +15,11 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// sqlserverFieldTypes
 const sqlserverFieldTypes = [
   'char',
   'varchar',
@@ -59,144 +51,129 @@ const sqlserverFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:sqlserver://127.0.0.1:1433;database=db_name',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.SQLServer.SchemaName'),
-      name: 'schemaName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.SQLServer.ServerTimezone'),
-      name: 'serverTimezone',
-      initialValue: 'UTC',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.SQLServer.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.SQLServer.PrimaryKey'),
-      name: 'primaryKey',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.SQLServer.AllMigration'),
-      name: 'allMigration',
-      rules: [{ required: true }],
-      initialValue: true,
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: true,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: false,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      name: 'username',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      name: 'password',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const sqlServer: FieldItemType[] = [
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:sqlserver://127.0.0.1:1433;database=db_name',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.SQLServer.SchemaName'),
+    name: 'schemaName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.SQLServer.ServerTimezone'),
+    name: 'serverTimezone',
+    initialValue: 'UTC',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.SQLServer.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.SQLServer.PrimaryKey'),
+    name: 'primaryKey',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.SQLServer.AllMigration'),
+    name: 'allMigration',
+    rules: [{ required: true }],
+    initialValue: true,
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: true,
+        },
+        {
+          label: i18n.t('basic.No'),
+          value: false,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    name: 'username',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    name: 'password',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -211,7 +188,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
@@ -221,7 +198,7 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       type: 'select',
       props: (text, record, idx, isNew) => ({
         options: sqlserverFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -262,13 +239,5 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'fieldComment',
       initialValue: '',
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const sqlServer = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sinks/tdsqlPostgreSql.tsx b/inlong-dashboard/src/metas/sinks/tdsqlPostgreSql.tsx
index daf553406..a790336f7 100644
--- a/inlong-dashboard/src/metas/sinks/tdsqlPostgreSql.tsx
+++ b/inlong-dashboard/src/metas/sinks/tdsqlPostgreSql.tsx
@@ -15,20 +15,12 @@
  * limitations under the License.
  */
 
-import React from 'react';
-import {
-  getColsFromFields,
-  GetStorageColumnsType,
-  GetStorageFormFieldsType,
-} from '@/utils/metaData';
-import { ColumnsType } from 'antd/es/table';
-import EditableTable, { ColumnsItemProps } from '@/components/EditableTable';
 import i18n from '@/i18n';
-import { excludeObject } from '@/utils';
+import type { FieldItemType } from '@/metas/common';
+import EditableTable from '@/components/EditableTable';
 import { sourceFields } from './common/sourceFields';
 
-// tdsqlpostgreSQLFieldTypes
-const tdsqlpostgreSQLFieldTypes = [
+const tdsqlPostgreSQLFieldTypes = [
   'SMALLINT',
   'SMALLSERIAL',
   'INT2',
@@ -57,113 +49,98 @@ const tdsqlpostgreSQLFieldTypes = [
   value: item,
 }));
 
-const getForm: GetStorageFormFieldsType = (
-  type,
-  { currentValues, inlongGroupId, isEdit, dataType, form } = {} as any,
-) => {
-  const fileds = [
-    {
-      type: 'input',
-      label: 'JDBC URL',
-      name: 'jdbcUrl',
-      rules: [{ required: true }],
-      props: {
-        placeholder: 'jdbc:postgresql://127.0.0.1:5432/db_name',
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: { width: 500 },
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.TDSQLPostgreSQL.SchemaName'),
-      name: 'schemaName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.TDSQLPostgreSQL.TableName'),
-      name: 'tableName',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.TDSQLPostgreSQL.PrimaryKey'),
-      name: 'primaryKey',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'radio',
-      label: i18n.t('meta.Sinks.EnableCreateResource'),
-      name: 'enableCreateResource',
-      rules: [{ required: true }],
-      initialValue: 1,
-      tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        options: [
-          {
-            label: i18n.t('basic.Yes'),
-            value: 1,
-          },
-          {
-            label: i18n.t('basic.No'),
-            value: 0,
-          },
-        ],
-      },
-    },
-    {
-      type: 'input',
-      label: i18n.t('meta.Sinks.Username'),
-      name: 'username',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-      },
-      _inTable: true,
-    },
-    {
-      type: 'password',
-      label: i18n.t('meta.Sinks.Password'),
-      name: 'password',
-      rules: [{ required: true }],
-      props: {
-        disabled: isEdit && [110, 130].includes(currentValues?.status),
-        style: {
-          maxWidth: 500,
+export const tdsqlPostgreSQL: FieldItemType[] = [
+  {
+    type: 'input',
+    label: 'JDBC URL',
+    name: 'jdbcUrl',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      placeholder: 'jdbc:postgresql://127.0.0.1:5432/db_name',
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.TDSQLPostgreSQL.SchemaName'),
+    name: 'schemaName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.TDSQLPostgreSQL.TableName'),
+    name: 'tableName',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.TDSQLPostgreSQL.PrimaryKey'),
+    name: 'primaryKey',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+    _renderTable: true,
+  },
+  {
+    type: 'radio',
+    label: i18n.t('meta.Sinks.EnableCreateResource'),
+    name: 'enableCreateResource',
+    rules: [{ required: true }],
+    initialValue: 1,
+    tooltip: i18n.t('meta.Sinks.EnableCreateResourceHelp'),
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+      options: [
+        {
+          label: i18n.t('basic.Yes'),
+          value: 1,
         },
-      },
-    },
-    {
-      type: (
-        <EditableTable
-          size="small"
-          columns={getFieldListColumns(dataType, currentValues)}
-          canDelete={(record, idx, isNew) => !isEdit || isNew}
-        />
-      ),
-      name: 'sinkFieldList',
-    },
-  ];
-
-  return type === 'col'
-    ? getColsFromFields(fileds)
-    : fileds.map(item => excludeObject(['_inTable'], item));
-};
+        {
+          label: i18n.t('basic.No'),
+          value: 0,
+        },
+      ],
+    }),
+  },
+  {
+    type: 'input',
+    label: i18n.t('meta.Sinks.Username'),
+    name: 'username',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    type: 'password',
+    label: i18n.t('meta.Sinks.Password'),
+    name: 'password',
+    rules: [{ required: true }],
+    props: values => ({
+      disabled: [110, 130].includes(values?.status),
+    }),
+  },
+  {
+    name: 'sinkFieldList',
+    type: EditableTable,
+    props: values => ({
+      size: 'small',
+      editing: ![110, 130].includes(values?.status),
+      columns: getFieldListColumns(values),
+    }),
+  },
+];
 
-const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) => {
+const getFieldListColumns = sinkValues => {
   return [
     ...sourceFields,
     {
@@ -178,17 +155,17 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
         },
       ],
       props: (text, record, idx, isNew) => ({
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
     },
     {
       title: `TDSQLPOSTGRESQL${i18n.t('meta.Sinks.TDSQLPostgreSQL.FieldType')}`,
       dataIndex: 'fieldType',
-      initialValue: tdsqlpostgreSQLFieldTypes[0].value,
+      initialValue: tdsqlPostgreSQLFieldTypes[0].value,
       type: 'select',
       props: (text, record, idx, isNew) => ({
-        options: tdsqlpostgreSQLFieldTypes,
-        disabled: [110, 130].includes(currentValues?.status as number) && !isNew,
+        options: tdsqlPostgreSQLFieldTypes,
+        disabled: [110, 130].includes(sinkValues?.status as number) && !isNew,
       }),
       rules: [{ required: true }],
     },
@@ -229,13 +206,5 @@ const getFieldListColumns: GetStorageColumnsType = (dataType, currentValues) =>
       dataIndex: 'fieldComment',
       initialValue: '',
     },
-  ] as ColumnsItemProps[];
-};
-
-const tableColumns = getForm('col') as ColumnsType;
-
-export const tdsqlPostgreSQL = {
-  getForm,
-  getFieldListColumns,
-  tableColumns,
+  ];
 };
diff --git a/inlong-dashboard/src/metas/sources/index.ts b/inlong-dashboard/src/metas/sources/index.ts
index de0563cf5..e6817cf82 100644
--- a/inlong-dashboard/src/metas/sources/index.ts
+++ b/inlong-dashboard/src/metas/sources/index.ts
@@ -57,7 +57,7 @@ const defaultCommonFields: FieldItemType[] = [
   },
   {
     name: 'sourceType',
-    type: 'radio',
+    type: 'select',
     label: i18n.t('meta.Sources.Type'),
     rules: [{ required: true }],
     initialValue: allSources[0].value,
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataSources/index.tsx b/inlong-dashboard/src/pages/GroupDetail/DataSources/index.tsx
index 7b4c0f830..ffb358263 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataSources/index.tsx
+++ b/inlong-dashboard/src/pages/GroupDetail/DataSources/index.tsx
@@ -41,21 +41,8 @@ const getFilterFormContent = defaultValues =>
         allowClear: true,
       },
     },
-    {
-      type: 'radiobutton',
-      name: 'sourceType',
-      label: i18n.t('meta.Sources.Type'),
-      initialValue: defaultValues.sourceType,
-      props: {
-        buttonStyle: 'solid',
-        options: sources.map(item => ({
-          label: item.label,
-          value: item.value,
-        })),
-      },
-    },
   ].concat(
-    pickObjectArray(['status'], sources[0].form).map(item => ({
+    pickObjectArray(['sourceType', 'status'], sources[0].form).map(item => ({
       ...item,
       visible: true,
       initialValue: defaultValues[item.name],
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStorage/DetailModal.tsx b/inlong-dashboard/src/pages/GroupDetail/DataStorage/DetailModal.tsx
index dc6d440a0..e4a70284f 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataStorage/DetailModal.tsx
+++ b/inlong-dashboard/src/pages/GroupDetail/DataStorage/DetailModal.tsx
@@ -18,12 +18,12 @@
  */
 
 import React, { useMemo, useState, useCallback } from 'react';
-import { Modal, message } from 'antd';
+import { Skeleton, Modal, message } from 'antd';
 import { ModalProps } from 'antd/es/modal';
 import { useRequest, useUpdateEffect } from '@/hooks';
 import { useTranslation } from 'react-i18next';
 import FormGenerator, { useForm, FormItemProps } from '@/components/FormGenerator';
-import { Sinks, SinkType } from '@/metas/sinks';
+import { sinks } from '@/metas/sinks';
 import request from '@/utils/request';
 
 export interface DetailModalProps extends ModalProps {
@@ -34,7 +34,7 @@ export interface DetailModalProps extends ModalProps {
   onOk?: (values) => void;
 }
 
-const SinksMap: Record<string, SinkType> = Sinks.reduce(
+const sinksMap: Record<string, typeof sinks[0]> = sinks.reduce(
   (acc, cur) => ({
     ...acc,
     [cur.value]: cur,
@@ -47,13 +47,13 @@ const Comp: React.FC<DetailModalProps> = ({ inlongGroupId, id, ...modalProps })
 
   const { t } = useTranslation();
 
-  const [currentValues, setCurrentValues] = useState({});
-
-  const [sinkType, setSinkType] = useState(Sinks[0].value);
+  // Q: Why sinkType default = '' ?
+  // A: Avoid the table of the fields triggering the monitoring of the column change.
+  const [sinkType, setSinkType] = useState('');
 
   const toFormVals = useCallback(
     v => {
-      const mapFunc = SinksMap[sinkType]?.toFormValues;
+      const mapFunc = sinksMap[sinkType]?.toFormValues;
       return mapFunc ? mapFunc(v) : v;
     },
     [sinkType],
@@ -61,26 +61,26 @@ const Comp: React.FC<DetailModalProps> = ({ inlongGroupId, id, ...modalProps })
 
   const toSubmitVals = useCallback(
     v => {
-      const mapFunc = SinksMap[sinkType]?.toSubmitValues;
+      const mapFunc = sinksMap[sinkType]?.toSubmitValues;
       return mapFunc ? mapFunc(v) : v;
     },
     [sinkType],
   );
 
-  const { data, run: getData } = useRequest(
+  const {
+    data,
+    loading,
+    run: getData,
+  } = useRequest(
     id => ({
       url: `/sink/get/${id}`,
-      params: {
-        sinkType,
-      },
     }),
     {
       manual: true,
       formatResult: result => toFormVals(result),
       onSuccess: result => {
-        form.setFieldsValue(result);
-        setCurrentValues(result);
         setSinkType(result.sinkType);
+        form.setFieldsValue(result);
       },
     },
   );
@@ -91,22 +91,16 @@ const Comp: React.FC<DetailModalProps> = ({ inlongGroupId, id, ...modalProps })
       if (id) {
         getData(id);
       } else {
-        form.resetFields(); // Note that it will cause the form to remount to initiate a select request
-        setSinkType(Sinks[0].value);
+        setSinkType(sinks[0].value);
       }
     } else {
-      setCurrentValues({});
+      form.resetFields();
+      setSinkType('');
     }
   }, [modalProps.visible]);
 
   const formContent = useMemo(() => {
-    const getForm = SinksMap[sinkType].getForm;
-    const config = getForm('form', {
-      currentValues,
-      inlongGroupId,
-      isEdit: !!id,
-      form,
-    }) as FormItemProps[];
+    const currentForm = sinksMap[sinkType]?.form || [];
     return [
       {
         type: 'select',
@@ -134,45 +128,9 @@ const Comp: React.FC<DetailModalProps> = ({ inlongGroupId, id, ...modalProps })
           },
         },
         rules: [{ required: true }],
-      },
-      {
-        name: 'sinkName',
-        type: 'input',
-        label: t('meta.Sinks.SinkName'),
-        rules: [
-          { required: true },
-          {
-            pattern: /^[a-zA-Z][a-zA-Z0-9_-]*$/,
-            message: t('meta.Sinks.SinkNameRule'),
-          },
-        ],
-        props: {
-          disabled: !!id,
-        },
-      },
-      {
-        name: 'sinkType',
-        type: 'select',
-        label: t('meta.Sinks.SinkType'),
-        rules: [{ required: true }],
-        initialValue: Sinks[0].value,
-        props: {
-          disabled: !!id,
-          options: Sinks,
-          onChange: value => setSinkType(value),
-        },
-      },
-      {
-        name: 'description',
-        type: 'textarea',
-        label: t('meta.Sinks.Description'),
-        props: {
-          showCount: true,
-          maxLength: 300,
-        },
       } as FormItemProps,
-    ].concat(config);
-  }, [sinkType, inlongGroupId, id, currentValues, form, t]);
+    ].concat(currentForm);
+  }, [sinkType, inlongGroupId, id, t]);
 
   const onOk = async () => {
     const values = await form.validateFields();
@@ -195,20 +153,20 @@ const Comp: React.FC<DetailModalProps> = ({ inlongGroupId, id, ...modalProps })
     message.success(t('basic.OperatingSuccess'));
   };
 
-  const onValuesChangeHandler = (...rest) => {
-    setCurrentValues(prev => ({ ...prev, ...rest[1] }));
-  };
-
   return (
-    <Modal title={SinksMap[sinkType]?.label} width={1200} {...modalProps} onOk={onOk}>
-      <FormGenerator
-        labelCol={{ span: 4 }}
-        wrapperCol={{ span: 20 }}
-        content={formContent}
-        form={form}
-        initialValues={data}
-        onValuesChange={onValuesChangeHandler}
-      />
+    <Modal title={sinksMap[sinkType]?.label} width={1200} {...modalProps} onOk={onOk}>
+      {loading ? (
+        <Skeleton active />
+      ) : (
+        <FormGenerator
+          labelCol={{ span: 4 }}
+          wrapperCol={{ span: 12 }}
+          content={formContent}
+          form={form}
+          initialValues={id ? data : {}}
+          onValuesChange={(c, values) => setSinkType(values.sinkType)}
+        />
+      )}
     </Modal>
   );
 };
diff --git a/inlong-dashboard/src/pages/GroupDetail/DataStorage/index.tsx b/inlong-dashboard/src/pages/GroupDetail/DataStorage/index.tsx
index 22f7b3daf..7c28b4533 100644
--- a/inlong-dashboard/src/pages/GroupDetail/DataStorage/index.tsx
+++ b/inlong-dashboard/src/pages/GroupDetail/DataStorage/index.tsx
@@ -24,10 +24,10 @@ import { defaultSize } from '@/configs/pagination';
 import { useRequest } from '@/hooks';
 import i18n from '@/i18n';
 import DetailModal from './DetailModal';
-import { Sinks } from '@/metas/sinks';
+import { sinks } from '@/metas/sinks';
 import request from '@/utils/request';
+import { pickObjectArray } from '@/utils';
 import { CommonInterface } from '../common';
-import { statusList, genStatusTag } from './status';
 
 type Props = CommonInterface;
 
@@ -36,28 +36,11 @@ const getFilterFormContent = defaultValues => [
     type: 'inputsearch',
     name: 'keyword',
   },
-  {
-    type: 'select',
-    name: 'sinkType',
-    label: i18n.t('pages.GroupDetail.Sink.Type'),
-    initialValue: defaultValues.sinkType,
-    props: {
-      dropdownMatchSelectWidth: false,
-      options: Sinks.map(item => ({
-        label: item.label,
-        value: item.value,
-      })),
-    },
-  },
-  {
-    type: 'select',
-    name: 'status',
-    label: i18n.t('basic.Status'),
-    props: {
-      allowClear: true,
-      options: statusList,
-    },
-  },
+  ...pickObjectArray(['sinkType', 'status'], sinks[0].form).map(item => ({
+    ...item,
+    visible: true,
+    initialValue: defaultValues[item.name],
+  })),
 ];
 
 const Comp = ({ inlongGroupId, readonly }: Props, ref) => {
@@ -65,7 +48,7 @@ const Comp = ({ inlongGroupId, readonly }: Props, ref) => {
     keyword: '',
     pageSize: defaultSize,
     pageNum: 1,
-    sinkType: Sinks[0].value,
+    sinkType: sinks[0].value,
   });
 
   const [createModal, setCreateModal] = useState<Record<string, unknown>>({
@@ -134,10 +117,10 @@ const Comp = ({ inlongGroupId, readonly }: Props, ref) => {
 
   const columnsMap = useMemo(
     () =>
-      Sinks.reduce(
+      sinks.reduce(
         (acc, cur) => ({
           ...acc,
-          [cur.value]: cur.tableColumns,
+          [cur.value]: cur.table,
         }),
         {},
       ),
@@ -152,11 +135,6 @@ const Comp = ({ inlongGroupId, readonly }: Props, ref) => {
   ]
     .concat(columnsMap[options.sinkType])
     .concat([
-      {
-        title: i18n.t('basic.Status'),
-        dataIndex: 'status',
-        render: text => genStatusTag(text),
-      },
       {
         title: i18n.t('basic.Operating'),
         dataIndex: 'action',
diff --git a/inlong-dashboard/src/themes/cover.less b/inlong-dashboard/src/themes/cover.less
index 0635bc19a..e5db5f825 100644
--- a/inlong-dashboard/src/themes/cover.less
+++ b/inlong-dashboard/src/themes/cover.less
@@ -73,19 +73,6 @@ body {
     }
   }
 
-  .ant-input,
-  textarea.ant-input,
-  .ant-select {
-    max-width: 500px;
-  }
-
-  .ant-input-textarea-show-count::after {
-    float: none;
-    display: block;
-    max-width: 500px;
-    text-align: right;
-  }
-
   .ant-modal-footer,
   .ant-modal-confirm-btns {
     display: flex;