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

nicholasjiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git


The following commit(s) were added to refs/heads/main by this push:
     new 3738f70  [Feature] Introduce dynamic form (#84)
3738f70 is described below

commit 3738f708bcd772e849a3964241d3d508e6def1ff
Author: labbomb <[email protected]>
AuthorDate: Sat Oct 28 16:21:42 2023 +0800

    [Feature] Introduce dynamic form (#84)
---
 paimon-web-ui-new/package.json                     |   2 +
 paimon-web-ui-new/pnpm-lock.yaml                   |  15 ++-
 .../dynamic-form/fields/checkbox.ts}               |  34 ++++--
 .../components/dynamic-form/fields/get-field.ts    |  41 +++++++
 .../dynamic-form/fields/index.ts}                  |  13 +--
 .../dynamic-form/fields/input.ts}                  |  18 +--
 .../src/components/dynamic-form/fields/radio.ts    |  45 ++++++++
 .../dynamic-form/fields/select.ts}                 |  25 +++--
 .../dynamic-form/fields/switch.ts}                 |  25 +++--
 .../dynamic-form/get-elements-by-json.ts           |  49 ++++++++
 .../src/components/dynamic-form/index.tsx          |  71 ++++++++++++
 .../src/components/dynamic-form/types.ts           |  86 ++++++++++++++
 .../src/components/dynamic-form/use-form.ts        |  76 +++++++++++++
 paimon-web-ui-new/src/components/modal/index.tsx   | 123 +++++++++++++++++++++
 paimon-web-ui-new/src/components/modal/use-task.ts |  61 ++++++++++
 .../cdc => }/components/table-action/index.tsx     |   0
 .../en/modules/layout.ts => form-lib/index.ts}     |   9 +-
 .../src/form-lib/source/use-cdc-list.ts            |  76 +++++++++++++
 paimon-web-ui-new/src/locales/en/modules/cdc.ts    |   5 +
 paimon-web-ui-new/src/locales/en/modules/layout.ts |   4 +-
 paimon-web-ui-new/src/locales/zh/modules/cdc.ts    |   5 +
 paimon-web-ui-new/src/locales/zh/modules/layout.ts |   4 +-
 .../src/views/cdc/components/list/index.tsx        |  19 +++-
 23 files changed, 754 insertions(+), 52 deletions(-)

diff --git a/paimon-web-ui-new/package.json b/paimon-web-ui-new/package.json
index 6fb7ba9..27fd659 100644
--- a/paimon-web-ui-new/package.json
+++ b/paimon-web-ui-new/package.json
@@ -13,6 +13,7 @@
   },
   "dependencies": {
     "dart-sass": "^1.25.0",
+    "lodash": "^4.17.21",
     "mitt": "^3.0.1",
     "monaco-editor": "^0.43.0",
     "pinia": "^2.1.6",
@@ -27,6 +28,7 @@
   "devDependencies": {
     "@rushstack/eslint-patch": "^1.3.3",
     "@tsconfig/node18": "^18.2.2",
+    "@types/lodash": "^4.14.200",
     "@types/node": "^18.17.15",
     "@varlet/axle": "^0.1.2",
     "@vicons/ionicons5": "^0.12.0",
diff --git a/paimon-web-ui-new/pnpm-lock.yaml b/paimon-web-ui-new/pnpm-lock.yaml
index eb3e50d..6252459 100644
--- a/paimon-web-ui-new/pnpm-lock.yaml
+++ b/paimon-web-ui-new/pnpm-lock.yaml
@@ -23,6 +23,9 @@ dependencies:
   dart-sass:
     specifier: ^1.25.0
     version: 1.25.0
+  lodash:
+    specifier: ^4.17.21
+    version: 4.17.21
   mitt:
     specifier: ^3.0.1
     version: 3.0.1
@@ -61,6 +64,9 @@ devDependencies:
   '@tsconfig/node18':
     specifier: ^18.2.2
     version: 18.2.2
+  '@types/lodash':
+    specifier: ^4.14.200
+    version: 4.14.200
   '@types/node':
     specifier: ^18.17.15
     version: 18.17.15
@@ -845,11 +851,11 @@ packages:
   /@types/[email protected]:
     resolution: {integrity: 
sha512-ZTcmhiI3NNU7dEvWLZJkzG6ao49zOIjEgIE0RgV7wbPxU0f2xT3VSAHw2gmst8swH6V0YkLRGp4qPlX/6I90MQ==}
     dependencies:
-      '@types/lodash': 4.14.198
+      '@types/lodash': 4.14.200
     dev: true
 
-  /@types/[email protected]:
-    resolution: {integrity: 
sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==}
+  /@types/[email protected]:
+    resolution: {integrity: 
sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==}
     dev: true
 
   /@types/[email protected]:
@@ -2709,7 +2715,6 @@ packages:
 
   /[email protected]:
     resolution: {integrity: 
sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-    dev: true
 
   /[email protected]:
     resolution: {integrity: 
sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
@@ -2821,7 +2826,7 @@ packages:
       '@css-render/plugin-bem': 0.15.12([email protected])
       '@css-render/vue3-ssr': 0.15.12([email protected])
       '@types/katex': 0.14.0
-      '@types/lodash': 4.14.198
+      '@types/lodash': 4.14.200
       '@types/lodash-es': 4.17.9
       async-validator: 4.2.5
       css-render: 0.15.12
diff --git a/paimon-web-ui-new/src/locales/zh/modules/layout.ts 
b/paimon-web-ui-new/src/components/dynamic-form/fields/checkbox.ts
similarity index 51%
copy from paimon-web-ui-new/src/locales/zh/modules/layout.ts
copy to paimon-web-ui-new/src/components/dynamic-form/fields/checkbox.ts
index 313b4d3..980a31f 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/layout.ts
+++ b/paimon-web-ui-new/src/components/dynamic-form/fields/checkbox.ts
@@ -15,11 +15,31 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export default {
-  playground: '查询控制台',
-  metadata: '元数据管理',
-  cdc_ingestion: 'CDC 集成',
-  system: '系统管理',
-  light: '浅色',
-  dark: '暗色'
+import { NCheckbox, NCheckboxGroup, NSpace } from 'naive-ui'
+import { isFunction } from 'lodash'
+import type { IJsonItem } from '../types'
+
+export function renderCheckbox(
+  item: IJsonItem,
+  fields: { [field: string]: any }
+) {
+  const { props, field, options } = isFunction(item) ? item() : item
+  if (!options) {
+    return h(NCheckbox, {
+      ...props,
+      value: fields[field],
+      onUpdateChecked: (checked: boolean) => void (fields[field] = checked)
+    })
+  }
+  return h(
+    NCheckboxGroup,
+    {
+      value: fields[field],
+      onUpdateValue: (value) => void (fields[field] = value)
+    },
+    () =>
+      h(NSpace, null, () =>
+        unref(options).map((option: object) => h(NCheckbox, { ...option }))
+      )
+  )
 }
diff --git a/paimon-web-ui-new/src/components/dynamic-form/fields/get-field.ts 
b/paimon-web-ui-new/src/components/dynamic-form/fields/get-field.ts
new file mode 100644
index 0000000..39bfa1e
--- /dev/null
+++ b/paimon-web-ui-new/src/components/dynamic-form/fields/get-field.ts
@@ -0,0 +1,41 @@
+/*
+ * 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 * as Field from './index'
+import { camelCase, upperFirst, isFunction } from 'lodash'
+import type { IFormRules, IJsonItem } from '../types'
+
+const TYPES = [
+  'input',
+  'radio',
+  'switch',
+  'select',
+  'checkbox',
+]
+
+const getField = (
+  item: IJsonItem,
+  fields: { [field: string]: any },
+  rules?: IFormRules
+) => {
+  const { type = 'input' } = isFunction(item) ? item() : item
+  if (!TYPES.includes(type)) return null
+  const renderTypeName = `render${upperFirst(camelCase(type))}`
+  // @ts-ignore
+  return Field[renderTypeName](item, fields)
+}
+
+export default getField
diff --git a/paimon-web-ui-new/src/locales/zh/modules/layout.ts 
b/paimon-web-ui-new/src/components/dynamic-form/fields/index.ts
similarity index 79%
copy from paimon-web-ui-new/src/locales/zh/modules/layout.ts
copy to paimon-web-ui-new/src/components/dynamic-form/fields/index.ts
index 313b4d3..477b947 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/layout.ts
+++ b/paimon-web-ui-new/src/components/dynamic-form/fields/index.ts
@@ -15,11 +15,8 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export default {
-  playground: '查询控制台',
-  metadata: '元数据管理',
-  cdc_ingestion: 'CDC 集成',
-  system: '系统管理',
-  light: '浅色',
-  dark: '暗色'
-}
+export { renderInput } from './input'
+export { renderRadio } from './radio'
+export { renderSwitch } from './switch'
+export { renderSelect } from './select'
+export { renderCheckbox } from './checkbox'
diff --git a/paimon-web-ui-new/src/locales/zh/modules/layout.ts 
b/paimon-web-ui-new/src/components/dynamic-form/fields/input.ts
similarity index 66%
copy from paimon-web-ui-new/src/locales/zh/modules/layout.ts
copy to paimon-web-ui-new/src/components/dynamic-form/fields/input.ts
index 313b4d3..5399825 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/layout.ts
+++ b/paimon-web-ui-new/src/components/dynamic-form/fields/input.ts
@@ -15,11 +15,15 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export default {
-  playground: '查询控制台',
-  metadata: '元数据管理',
-  cdc_ingestion: 'CDC 集成',
-  system: '系统管理',
-  light: '浅色',
-  dark: '暗色'
+import { isFunction } from 'lodash'
+import type { IJsonItem } from '../types'
+import { NInput } from 'naive-ui'
+
+export function renderInput(item: IJsonItem, fields: { [field: string]: any }) 
{
+  const { props, field } = isFunction(item) ? item() : item
+  return h(NInput, {
+    ...props,
+    value: fields[field],
+    onUpdateValue: (value: string) => void (fields[field] = value)
+  })
 }
diff --git a/paimon-web-ui-new/src/components/dynamic-form/fields/radio.ts 
b/paimon-web-ui-new/src/components/dynamic-form/fields/radio.ts
new file mode 100644
index 0000000..65d2bba
--- /dev/null
+++ b/paimon-web-ui-new/src/components/dynamic-form/fields/radio.ts
@@ -0,0 +1,45 @@
+/* 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 { NRadio, NRadioGroup, NSpace } from 'naive-ui'
+import { isFunction } from 'lodash'
+import type { IJsonItem, IOption } from '../types'
+
+export function renderRadio(item: IJsonItem, fields: { [field: string]: any }) 
{
+  const { props, field, options } = isFunction(item) ? item() : item
+  if (!options) {
+    return h(NRadio, {
+      ...props,
+      value: fields[field],
+      onUpdateChecked: (checked: boolean) => void (fields[field] = checked)
+    })
+  }
+  return h(
+    NRadioGroup,
+    {
+      ...props,
+      value: fields[field],
+      onUpdateValue: (value: any) => void (fields[field] = value)
+    },
+    () =>
+      h(NSpace, null, () =>
+        unref(options).map((option: IOption) =>
+          h(NRadio, option, () => option.label)
+        )
+      )
+  )
+}
diff --git a/paimon-web-ui-new/src/locales/zh/modules/layout.ts 
b/paimon-web-ui-new/src/components/dynamic-form/fields/select.ts
similarity index 60%
copy from paimon-web-ui-new/src/locales/zh/modules/layout.ts
copy to paimon-web-ui-new/src/components/dynamic-form/fields/select.ts
index 313b4d3..fabd2db 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/layout.ts
+++ b/paimon-web-ui-new/src/components/dynamic-form/fields/select.ts
@@ -15,11 +15,22 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export default {
-  playground: '查询控制台',
-  metadata: '元数据管理',
-  cdc_ingestion: 'CDC 集成',
-  system: '系统管理',
-  light: '浅色',
-  dark: '暗色'
+import { NSelect } from 'naive-ui'
+import { isFunction } from 'lodash'
+import type { IJsonItem } from '../types'
+
+export function renderSelect(
+  item: IJsonItem,
+  fields: { [field: string]: any }
+) {
+  const { props, field, options = [] } = isFunction(item) ? item() : item
+  return h(NSelect, {
+    ...props,
+    value: fields[field],
+    onUpdateValue: (value: any) => {
+      void (fields[field] = value)
+      if (props?.onUpdateValue) props.onUpdateValue(value)
+    },
+    options: unref(options)
+  })
 }
diff --git a/paimon-web-ui-new/src/locales/zh/modules/layout.ts 
b/paimon-web-ui-new/src/components/dynamic-form/fields/switch.ts
similarity index 63%
copy from paimon-web-ui-new/src/locales/zh/modules/layout.ts
copy to paimon-web-ui-new/src/components/dynamic-form/fields/switch.ts
index 313b4d3..5bf47dc 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/layout.ts
+++ b/paimon-web-ui-new/src/components/dynamic-form/fields/switch.ts
@@ -15,11 +15,22 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
-export default {
-  playground: '查询控制台',
-  metadata: '元数据管理',
-  cdc_ingestion: 'CDC 集成',
-  system: '系统管理',
-  light: '浅色',
-  dark: '暗色'
+import { NSwitch } from 'naive-ui'
+import { isFunction } from 'lodash'
+import type { IJsonItem } from '../types'
+
+export function renderSwitch(
+  item: IJsonItem,
+  fields: { [field: string]: any }
+) {
+  const { props, field, slots = {} } = isFunction(item) ? item() : item
+  return h(
+    NSwitch,
+    {
+      ...props,
+      value: fields[field],
+      onUpdateValue: (value: string) => void (fields[field] = value)
+    },
+    { ...slots }
+  )
 }
diff --git 
a/paimon-web-ui-new/src/components/dynamic-form/get-elements-by-json.ts 
b/paimon-web-ui-new/src/components/dynamic-form/get-elements-by-json.ts
new file mode 100644
index 0000000..5b22c8b
--- /dev/null
+++ b/paimon-web-ui-new/src/components/dynamic-form/get-elements-by-json.ts
@@ -0,0 +1,49 @@
+/* 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 { useFormHooks } from './use-form'
+import getField from './fields/get-field'
+import { omit, isFunction } from 'lodash'
+import type { IFormItem, IJsonItem, IFormRules } from './types'
+
+export default function getElementByJson(
+  json: IJsonItem[],
+  fields: { [field: string]: any }
+) {
+  const rules: IFormRules = {}
+  const initialValues: { [field: string]: any } = {}
+  const elements: IFormItem[] = []
+  for (const item of json) {
+    const mergedItem = isFunction(item) ? item() : item
+    const { name, value, field, children, validate, ...rest } = mergedItem
+    if (value || value === 0) {
+      fields[field] = value
+      initialValues[field] = value
+    }
+    if (validate) rules[field] = useFormHooks().formatValidate(validate)
+    const element: IFormItem = {
+      showLabel: !!name,
+      ...omit(rest, ['type', 'props', 'options']),
+      label: name,
+      path: !children ? field : '',
+      widget: () => getField(item, fields, rules),
+      span: toRef(mergedItem, 'span') as Ref<number>
+    }
+    elements.push(element)
+  }
+  return { rules, elements, initialValues }
+}
diff --git a/paimon-web-ui-new/src/components/dynamic-form/index.tsx 
b/paimon-web-ui-new/src/components/dynamic-form/index.tsx
new file mode 100644
index 0000000..97064a1
--- /dev/null
+++ b/paimon-web-ui-new/src/components/dynamic-form/index.tsx
@@ -0,0 +1,71 @@
+/* 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 type { FormRules, GridProps, IMeta } from "./types"
+import { useFormHooks } from "./use-form"
+
+const props = {
+  loading: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  },
+  gridProps: {
+    type: Object as PropType<GridProps>
+  },
+  meta: {
+    type: Object as PropType<IMeta>,
+    default: {},
+    required: true
+  },
+}
+
+export default defineComponent({
+  name: 'DynamicForm',
+  props,
+  setup(props, { expose }) {
+    const { state, ...rest } = useFormHooks()
+    expose({
+      ...rest
+    })
+    return { ...toRefs(state) }
+  },
+  render(props: { meta: IMeta; gridProps?: GridProps; loading?: boolean }) {
+    const { loading, gridProps, meta } = props
+    const { elements = [], ...restFormProps } = meta
+    return (
+      <n-spin show={loading}>
+        <n-form {...restFormProps} rules={meta.rules as FormRules} 
ref='formRef'>
+          <n-grid {...gridProps}>
+            {elements.map((element) => {
+              const { span = 24, path, widget, ...formItemProps } = element
+              return (
+                <n-form-item-gi
+                  {...formItemProps}
+                  span={unref(span) === void 0 ? 24 : unref(span)}
+                  path={path}
+                  key={path || String(Date.now() + Math.random())}
+                >
+                  {h(widget)}
+                </n-form-item-gi>
+              )
+            })}
+          </n-grid>
+        </n-form>
+      </n-spin>
+    )
+  }
+})
diff --git a/paimon-web-ui-new/src/components/dynamic-form/types.ts 
b/paimon-web-ui-new/src/components/dynamic-form/types.ts
new file mode 100644
index 0000000..6ee0c6a
--- /dev/null
+++ b/paimon-web-ui-new/src/components/dynamic-form/types.ts
@@ -0,0 +1,86 @@
+/* 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 type { FormItemRule, FormProps, FormRules, GridProps } from "naive-ui"
+
+type ComponentType =
+  | 'input'
+  | 'radio'
+  | 'switch'
+  | 'select'
+  | 'checkbox'
+
+
+interface IOption {
+  [key: string]: any
+}
+
+interface IFormItemRule extends Omit<FormItemRule, 'required'> {
+  required?: boolean | Ref<boolean>
+}
+
+interface IJsonItemParams {
+  type: ComponentType
+  field: string
+  name?: string
+  value?: any
+  props?: any
+  options?: IOption[] | Ref<IOption[]>
+  span?: number
+  children?: IJsonItem[]
+  validate?: IFormItemRule
+  slots?: object
+}
+
+type IJsonItem = IJsonItemParams | IJsonItemFn
+
+type IJsonItemFn = (i?: number) => IJsonItemParams
+
+interface IFormItem {
+  showLabel?: boolean
+  path: string
+  label?: string
+  widget: any
+  span?: number | Ref<number>
+  class?: string
+}
+
+type IFormRules =
+  | {
+      [path: string]: IFormItemRule | IFormItemRule[]
+    }
+  | FormRules
+
+interface IMeta extends Omit<FormProps, 'model' | 'rules'> {
+  elements?: IFormItem[]
+  model: object
+  rules: IFormRules
+}
+
+export type {
+  IJsonItem,
+  IJsonItemParams,
+  IJsonItemFn,
+  IOption,
+  ComponentType,
+  IFormItem,
+  IFormRules,
+  IFormItemRule,
+  IMeta,
+  GridProps,
+  FormRules
+}
diff --git a/paimon-web-ui-new/src/components/dynamic-form/use-form.ts 
b/paimon-web-ui-new/src/components/dynamic-form/use-form.ts
new file mode 100644
index 0000000..e508191
--- /dev/null
+++ b/paimon-web-ui-new/src/components/dynamic-form/use-form.ts
@@ -0,0 +1,76 @@
+/* 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 type { FormRules, IFormItemRule } from "./types"
+
+export const useFormHooks = () => {
+  const state = reactive({
+    formRef: ref()
+  })
+
+  const validate = async (...args: []) => {
+    await state.formRef.validate(...args)
+  }
+
+  const setValues = (initialValues: { [field: string]: any }) => {
+    for (const [key, value] of Object.entries(initialValues)) {
+      state.formRef.model[key] = value
+    }
+  }
+
+  const restoreValidation = () => {
+    state.formRef.restoreValidation()
+  }
+
+  const resetValues = (initialValues: { [field: string]: any }) => {
+    const modelKeys = Object.keys(state.formRef.model)
+    for (const key of modelKeys) {
+      if (!Object.keys(initialValues).includes(key)) {
+        delete state.formRef.model[key]
+      }
+    }
+    setValues(initialValues)
+  }
+
+  const getValues = () => {
+    return state.formRef.model
+  }
+
+  const formatValidate = (
+    validate?: IFormItemRule | FormRules
+  ): IFormItemRule => {
+    if (!validate) return {}
+    if (Array.isArray(validate)) {
+      validate.forEach((item: IFormItemRule) => {
+        if (!item?.message) delete item.message
+        return item
+      })
+    }
+    if (!validate.message) delete validate.message
+    return validate
+  }
+
+  return {
+    state,
+    validate,
+    setValues,
+    getValues,
+    resetValues,
+    restoreValidation,
+    formatValidate
+  }
+}
diff --git a/paimon-web-ui-new/src/components/modal/index.tsx 
b/paimon-web-ui-new/src/components/modal/index.tsx
new file mode 100644
index 0000000..6aaeec4
--- /dev/null
+++ b/paimon-web-ui-new/src/components/modal/index.tsx
@@ -0,0 +1,123 @@
+/* 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 Form from "@/components/dynamic-form"
+import { useTask } from "./use-task"
+
+const props = {
+  title: {
+    type: String as PropType<string>,
+    default: ''
+  },
+  row: {
+    type: Object as PropType<any>,
+    default: () => {}
+  },
+  showModal: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  },
+  autoFocus: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  },
+  closeable: {
+    type: Boolean as PropType<boolean>,
+    default: true
+  },
+  formType: {
+    type: String as PropType<string>,
+    default: ''
+  }
+}
+
+export default defineComponent({
+  name: 'ModalPage',
+  props,
+  emits: ['confirm', 'cancel'],
+  setup(props, { expose, emit }) {
+    const { t } = useLocaleHooks()
+    const formRef = ref()
+    expose({formRef})
+
+    const { elementsRef, rulesRef, model } = useTask({
+      data: props.row,
+      formType: props.formType
+    })
+
+    const handleConfirm = () => {
+      emit('confirm')
+    }
+
+    const handleCancel = () => {
+      emit('cancel')
+    }
+
+    return {
+      t,
+      handleConfirm,
+      handleCancel,
+      formRef,
+      elementsRef,
+      rulesRef,
+      model
+    }
+  },
+  render () {
+    return (
+      <n-modal
+        v-model:show={this.showModal}
+        mask-closable={false}
+        auto-focus={this.autoFocus}
+      >
+        <n-card
+          style="width: 600px"
+          title={this.title}
+          bordered={false}
+          closable={this.closeable}
+          on-close={this.handleCancel}
+        >
+          {{
+            default: () => (
+              <Form
+                ref={this.formRef}
+                meta={{
+                  ...this.model,
+                  rules: this.rulesRef,
+                  elements: this.elementsRef,
+                }}
+                gridProps={{
+                  xGap: 10
+                }}
+              />
+            ),
+            footer: () => (
+              <n-space justify='end'>
+                <n-button onClick={this.handleCancel}>
+                  {this.t('layout.cancel')}
+                </n-button>
+                <n-button type='primary' onClick={this.handleConfirm}>
+                  {this.t('layout.confirm')}
+                </n-button>
+              </n-space>
+            )
+          }}
+        </n-card>
+      </n-modal>
+    )
+  }
+})
diff --git a/paimon-web-ui-new/src/components/modal/use-task.ts 
b/paimon-web-ui-new/src/components/modal/use-task.ts
new file mode 100644
index 0000000..4d2a415
--- /dev/null
+++ b/paimon-web-ui-new/src/components/modal/use-task.ts
@@ -0,0 +1,61 @@
+/* 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 tasks from '@/form-lib'
+import getElementByJson from '@/components/dynamic-form/get-elements-by-json'
+import type {
+  IFormItem,
+  IJsonItem,
+  FormRules
+} from '@/components/dynamic-form/types'
+
+export function useTask({
+  data,
+  formType
+}: {
+  data: any
+  formType: string
+}): {
+  elementsRef: Ref<IFormItem[]>
+  rulesRef: Ref<FormRules>
+  model: any
+} {
+  const jsonRef = ref([]) as Ref<IJsonItem[]>
+  const elementsRef = ref([]) as Ref<IFormItem[]>
+  const rulesRef = ref({})
+
+  const params = {
+    data,
+    jsonRef,
+    updateElements: () => {
+      getElements()
+    }
+  }
+
+  const task = tasks[formType as keyof typeof tasks]
+  const { model, json } = task(params)
+  jsonRef.value = json
+  const getElements = () => {
+    const { rules, elements } = getElementByJson(jsonRef.value, model)
+    elementsRef.value = elements
+    rulesRef.value = rules
+  }
+
+  getElements()
+
+  return { elementsRef, rulesRef, model }
+}
diff --git a/paimon-web-ui-new/src/views/cdc/components/table-action/index.tsx 
b/paimon-web-ui-new/src/components/table-action/index.tsx
similarity index 100%
rename from paimon-web-ui-new/src/views/cdc/components/table-action/index.tsx
rename to paimon-web-ui-new/src/components/table-action/index.tsx
diff --git a/paimon-web-ui-new/src/locales/en/modules/layout.ts 
b/paimon-web-ui-new/src/form-lib/index.ts
similarity index 84%
copy from paimon-web-ui-new/src/locales/en/modules/layout.ts
copy to paimon-web-ui-new/src/form-lib/index.ts
index 31d6e66..8d366d9 100644
--- a/paimon-web-ui-new/src/locales/en/modules/layout.ts
+++ b/paimon-web-ui-new/src/form-lib/index.ts
@@ -15,11 +15,8 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License. */
 
+import { useCDCList } from "./source/use-cdc-list";
+
 export default {
-  playground: 'Playground',
-  metadata: 'MetaData',
-  cdc_ingestion: 'CDC Ingestion',
-  system: 'System',
-  light: 'Light',
-  dark: 'Dark'
+  CDCLIST: useCDCList
 }
diff --git a/paimon-web-ui-new/src/form-lib/source/use-cdc-list.ts 
b/paimon-web-ui-new/src/form-lib/source/use-cdc-list.ts
new file mode 100644
index 0000000..6129a2c
--- /dev/null
+++ b/paimon-web-ui-new/src/form-lib/source/use-cdc-list.ts
@@ -0,0 +1,76 @@
+/* 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 type { IJsonItem } from "@/components/dynamic-form/types"
+
+export function useCDCList(item:any) {
+  const { t } = useLocaleHooks()
+
+       const model = reactive({
+               name: item.name,
+               description: item.description,
+               synchronizationType: item.synchronizationType,
+       })
+
+       const synchronizationTypeOptions = [
+               {
+                       label: t('cdc.single_table_synchronization'),
+                       value: 0
+               },
+               {
+                       label: t('cdc.whole_database_synchronization'),
+                       value: 1
+               },
+       ]
+
+       return {
+               json: [
+                       {
+                               type: 'input',
+                               field: 'name',
+                               name: t('cdc.synchronization_job_name'),
+                               props: {
+                                       placeholder: ''
+                               },
+                               validate: {
+                                       trigger: ['input', 'blur'],
+                                       required: true,
+                                       message: 'error',
+                                       validator: (validator: any, value: 
string) => {
+                                               if (!value) {
+                                                       return new 
Error('error')
+                                               }
+                                       }
+                               }
+                       },
+                       {
+                               type: 'input',
+                               field: 'description',
+                               name: t('cdc.task_description'),
+                               props: {
+                                       placeholder: ''
+                               }
+                       },
+                       {
+                               type: 'radio',
+                               field: 'synchronizationType',
+                               name: t('cdc.synchronization_type'),
+                               options: synchronizationTypeOptions
+                       },
+               ] as IJsonItem[], model
+       }
+}
diff --git a/paimon-web-ui-new/src/locales/en/modules/cdc.ts 
b/paimon-web-ui-new/src/locales/en/modules/cdc.ts
index a5487e9..b8d2ca3 100644
--- a/paimon-web-ui-new/src/locales/en/modules/cdc.ts
+++ b/paimon-web-ui-new/src/locales/en/modules/cdc.ts
@@ -28,4 +28,9 @@ export default {
   edit: 'Edit',
   run: 'Run',
   delete: 'Delete',
+  synchronization_job_name: 'Synchronization Job Name',
+  edit_synchronization_job: 'Edit Synchronization Job',
+  task_description: 'Task Description',
+  single_table_synchronization: 'Single Table Synchronization',
+  whole_database_synchronization: 'Whole Database Synchronization',
 }
diff --git a/paimon-web-ui-new/src/locales/en/modules/layout.ts 
b/paimon-web-ui-new/src/locales/en/modules/layout.ts
index 31d6e66..d6e8f26 100644
--- a/paimon-web-ui-new/src/locales/en/modules/layout.ts
+++ b/paimon-web-ui-new/src/locales/en/modules/layout.ts
@@ -21,5 +21,7 @@ export default {
   cdc_ingestion: 'CDC Ingestion',
   system: 'System',
   light: 'Light',
-  dark: 'Dark'
+  dark: 'Dark',
+  cancel: 'Cancel',
+  confirm: 'Confirm',
 }
diff --git a/paimon-web-ui-new/src/locales/zh/modules/cdc.ts 
b/paimon-web-ui-new/src/locales/zh/modules/cdc.ts
index 6cb6a0a..f80b3d0 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/cdc.ts
+++ b/paimon-web-ui-new/src/locales/zh/modules/cdc.ts
@@ -28,4 +28,9 @@ export default {
   edit: '编辑',
   run: '运行',
   delete: '删除',
+  synchronization_job_name: '同步作业名称',
+  edit_synchronization_job: '编辑同步作业',
+  task_description: '任务描述',
+  single_table_synchronization: '单表同步',
+  whole_database_synchronization: '整库同步',
 }
diff --git a/paimon-web-ui-new/src/locales/zh/modules/layout.ts 
b/paimon-web-ui-new/src/locales/zh/modules/layout.ts
index 313b4d3..e7e09ae 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/layout.ts
+++ b/paimon-web-ui-new/src/locales/zh/modules/layout.ts
@@ -21,5 +21,7 @@ export default {
   cdc_ingestion: 'CDC 集成',
   system: '系统管理',
   light: '浅色',
-  dark: '暗色'
+  dark: '暗色',
+  cancel: '取消',
+  confirm: '确认',
 }
diff --git a/paimon-web-ui-new/src/views/cdc/components/list/index.tsx 
b/paimon-web-ui-new/src/views/cdc/components/list/index.tsx
index 0031ac4..568cd4f 100644
--- a/paimon-web-ui-new/src/views/cdc/components/list/index.tsx
+++ b/paimon-web-ui-new/src/views/cdc/components/list/index.tsx
@@ -16,7 +16,8 @@ specific language governing permissions and limitations
 under the License. */
 
 import styles from './index.module.scss';
-import TableAction from '../table-action';
+import TableAction from '../../../../components/table-action';
+import Modal from '@/components/modal';
 
 export default defineComponent({
   name: 'ListPage',
@@ -60,6 +61,10 @@ export default defineComponent({
           render: (row: any) =>
             h(TableAction, {
               row,
+              onHandleEdit: (row) => {
+                tableVariables.showModal = true
+                tableVariables.row = row
+              },
             })
         }
       ],
@@ -69,7 +74,9 @@ export default defineComponent({
       ],
       pagination: {
         pageSize: 10
-      }
+      },
+      showModal: false,
+      row: {}
     })
     return {
       t,
@@ -83,7 +90,13 @@ export default defineComponent({
           columns={this.columns}
           data={this.data}
           pagination={this.pagination}
-          bordered="false"
+          bordered={false}
+        />
+        <Modal
+          showModal={this.showModal}
+          title={this.t('cdc.edit_synchronization_job')}
+          formType="CDCLIST"
+          onCancel={() => this.showModal = false}
         />
       </div>
     )


Reply via email to