You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@seatunnel.apache.org by so...@apache.org on 2023/05/05 11:20:18 UTC

[incubator-seatunnel-web] branch add_canvas_job_define updated: [Feat][UI] Add component of dynamic form in this project.

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

songjian pushed a commit to branch add_canvas_job_define
in repository https://gitbox.apache.org/repos/asf/incubator-seatunnel-web.git


The following commit(s) were added to refs/heads/add_canvas_job_define by this push:
     new dfa56fde [Feat][UI] Add component of dynamic form in this project.
dfa56fde is described below

commit dfa56fde1e5040ab15465d58a0d7c42439c8fbf0
Author: songjianet <17...@qq.com>
AuthorDate: Fri May 5 20:20:05 2023 +0900

    [Feat][UI] Add component of dynamic form in this project.
---
 .../components/dynamic-form/dynamic-form-item.tsx  | 165 +++++++++++++++++++++
 .../src/components/dynamic-form/use-form-field.ts  |  32 ++++
 .../components/dynamic-form/use-form-locales.ts    |  26 ++++
 .../components/dynamic-form/use-form-request.ts    |  39 +++++
 .../components/dynamic-form/use-form-structure.ts  |  25 ++++
 .../components/dynamic-form/use-form-validate.ts   |  69 +++++++++
 6 files changed, 356 insertions(+)

diff --git a/seatunnel-ui/src/components/dynamic-form/dynamic-form-item.tsx b/seatunnel-ui/src/components/dynamic-form/dynamic-form-item.tsx
new file mode 100644
index 00000000..e29727fb
--- /dev/null
+++ b/seatunnel-ui/src/components/dynamic-form/dynamic-form-item.tsx
@@ -0,0 +1,165 @@
+/*
+ * 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 { defineComponent } from 'vue'
+import {
+  NFormItemGi,
+  NGrid,
+  NInput,
+  NSelect,
+  NCheckboxGroup,
+  NSpace,
+  NCheckbox
+} from 'naive-ui'
+import { useI18n } from 'vue-i18n'
+import type { PropType } from 'vue'
+import type { SelectOption } from 'naive-ui'
+
+const props = {
+  formStructure: {
+    type: Object as PropType<Array<object>>
+  },
+  model: {
+    type: Object as PropType<object>
+  },
+  name: {
+    type: String as PropType<string>,
+    default: ''
+  },
+  locales: {
+    type: Object as PropType<object>
+  }
+}
+
+const DynamicFormItem = defineComponent({
+  name: 'DynamicFormItem',
+  props,
+  setup(props) {
+    const { t } = useI18n()
+
+    if (props.locales) {
+      useI18n().mergeLocaleMessage('zh_CN', {
+        i18n: (props.locales as any).zh_CN
+      })
+      useI18n().mergeLocaleMessage('en_US', {
+        i18n: (props.locales as any).en_US
+      })
+    }
+
+    const formatClass = (name: string, modelField: string) => {
+      return name.indexOf('[') >= 0
+        ? name.split('[')[0].toLowerCase() + name.split('[')[1].split(']')[0]
+        : name.toLowerCase() + '-' + modelField.toLowerCase()
+    }
+
+    const formItemDisabled = (field: string, value: Array<any>) => {
+      return value.map((v) => field === v).indexOf(false) < 0
+    }
+
+    return {
+      t,
+      formatClass,
+      formItemDisabled
+    }
+  },
+  render() {
+    return (
+      <NGrid xGap={10}>
+        {(this.formStructure as Array<any>).map((f) => {
+          return (
+            (f.show
+              ? this.formItemDisabled(
+                  (this.model as any)[f.show.field],
+                  f.show.value
+                )
+              : true) && (
+              <NFormItemGi
+                label={this.t(f.label)}
+                path={f.field}
+                span={f.span || 24}
+              >
+                {f.type === 'input' && (
+                  <NInput
+                    class={`dynamic-form_${this.formatClass(
+                      this.name,
+                      f.field
+                    )}`}
+                    placeholder={f.placeholder ? this.t(f.placeholder) : ''}
+                    v-model={[(this.model as any)[f.field], 'value']}
+                    clearable={f.clearable}
+                    type={f.inputType}
+                    rows={f.row ? f.row : 4}
+                  />
+                )}
+                {f.type === 'select' &&
+                  (f.show
+                    ? this.formItemDisabled(
+                        (this.model as any)[f.show.field],
+                        f.show.value
+                      )
+                    : true) && (
+                    <NSelect
+                      class={`dynamic-form_${this.formatClass(
+                        this.name,
+                        f.field
+                      )}`}
+                      placeholder={f.placeholder ? this.t(f.placeholder) : ''}
+                      v-model={[(this.model as any)[f.field], 'value']}
+                      options={
+                        f.options.map((o: SelectOption) => {
+                          return {
+                            label: this.t(o.label as string),
+                            value: o.value
+                          }
+                        })
+                      }
+                    />
+                  )}
+                {f.type === 'checkbox' &&
+                  (f.show
+                    ? this.formItemDisabled(
+                        (this.model as any)[f.show.field],
+                        f.show.value
+                      )
+                    : true) && (
+                    <NCheckboxGroup
+                      class={`dynamic-form_${this.formatClass(
+                        this.name,
+                        f.field
+                      )}`}
+                      v-model={[(this.model as any)[f.field], 'value']}
+                    >
+                      <NSpace vertical={f.vertical}>
+                        {f.options.map((o: any) => (
+                          <NCheckbox
+                            label={this.t(o.label as string)}
+                            value={o.value}
+                          />
+                        ))}
+                      </NSpace>
+                    </NCheckboxGroup>
+                  )}
+              </NFormItemGi>
+            )
+          )
+        })}
+      </NGrid>
+    )
+  }
+})
+
+export { DynamicFormItem }
diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-field.ts b/seatunnel-ui/src/components/dynamic-form/use-form-field.ts
new file mode 100644
index 00000000..3d6a0e97
--- /dev/null
+++ b/seatunnel-ui/src/components/dynamic-form/use-form-field.ts
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+export function useFormField(forms: Array<any>) {
+  const model: any = {}
+
+  const setField = (value: string, type: string): null | string => {
+    return value ? value : type === 'select' ? null : ''
+  }
+
+  forms.forEach((f: any) => {
+    if (!f.field) return
+
+    model[f.field] = setField(f.defaultValue, f.type)
+  })
+
+  return model
+}
diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-locales.ts b/seatunnel-ui/src/components/dynamic-form/use-form-locales.ts
new file mode 100644
index 00000000..90d8c7c0
--- /dev/null
+++ b/seatunnel-ui/src/components/dynamic-form/use-form-locales.ts
@@ -0,0 +1,26 @@
+/*
+ * 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 { useI18n } from 'vue-i18n'
+
+export function useFormLocales(locales: {
+  zh_CN: object
+  en_US: object
+}): void {
+  useI18n().mergeLocaleMessage('zh_CN', { i18n: locales.zh_CN })
+  useI18n().mergeLocaleMessage('en_US', { i18n: locales.en_US })
+}
diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-request.ts b/seatunnel-ui/src/components/dynamic-form/use-form-request.ts
new file mode 100644
index 00000000..6ba882e9
--- /dev/null
+++ b/seatunnel-ui/src/components/dynamic-form/use-form-request.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 { axios } from '@/service/service'
+
+const reqFunction = (url: string, method: string) => {
+  return axios({
+    url,
+    method
+  } as any)
+}
+
+export function useFormRequest(apis: any, forms: Array<any>): Array<any> {
+  forms.map(f => {
+    if (f.api) {
+      reqFunction(apis[f.api].url, apis[f.api].method).then((res: any) => {
+        f.options = res.map((r: any) => {
+          return { label: r.label, value: r.value }
+        })
+      })
+    }
+  })
+
+  return forms
+}
\ No newline at end of file
diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-structure.ts b/seatunnel-ui/src/components/dynamic-form/use-form-structure.ts
new file mode 100644
index 00000000..34166ac6
--- /dev/null
+++ b/seatunnel-ui/src/components/dynamic-form/use-form-structure.ts
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+export function useFormStructure(forms: Array<any>) {
+  return forms.map((f: any) => {
+    delete f.validate
+    delete f.api
+
+    return f
+  })
+}
\ No newline at end of file
diff --git a/seatunnel-ui/src/components/dynamic-form/use-form-validate.ts b/seatunnel-ui/src/components/dynamic-form/use-form-validate.ts
new file mode 100644
index 00000000..86be15ae
--- /dev/null
+++ b/seatunnel-ui/src/components/dynamic-form/use-form-validate.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 } from 'naive-ui'
+
+export function useFormValidate(forms: Array<any>, model: any, t: any) {
+  const validate: any = {}
+
+  const setValidate = (validate: any, field: string): object => {
+    const data: any = {
+      required: validate.required,
+      trigger: validate.trigger
+    }
+
+    if (validate.type === 'non-empty') {
+      data['validator'] = (rule: FormItemRule, value: string) => {
+        if (!model[field]) {
+          return Error(t(validate.message))
+        }
+      }
+    } else if (validate.type === 'union-non-empty') {
+      const fields = validate.fields.splice(
+        validate.fields.findIndex((f: any) => f !== field),
+        1
+      )
+      data['validator'] = (rule: FormItemRule, value: string) => {
+        const fieldsValidate = fields.map((f: string) => !!model[f])
+        if (!value && fieldsValidate.includes(true)) {
+          return Error(t(validate.message))
+        }
+      }
+    } else if (validate.type === 'mutually-exclusive') {
+      const fields = validate.fields.splice(
+        validate.fields.findIndex((f: any) => f !== field),
+        1
+      )
+      data['validator'] = (rule: FormItemRule, value: string) => {
+        const fieldsValidate = fields.map((f: string) => !!model[f])
+        if (value && fieldsValidate.indexOf(false) < 0) {
+          return Error(t(validate.message))
+        }
+      }
+    }
+
+    return data
+  }
+
+  forms.forEach((f: any) => {
+    if (!f.validate || Object.keys(f.validate).length <= 0) return
+
+    validate[f.field] = setValidate(f.validate, f.field)
+  })
+
+  return validate
+}