From 71baafecc7198610db9ecf76df4cc5c6b0626b4e Mon Sep 17 00:00:00 2001 From: luoer Date: Mon, 13 Nov 2023 16:59:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E9=92=A9=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/form/core/interface.ts | 9 ++ src/components/form/core/useFormItems.ts | 46 +++++++++ src/components/form/core/useFormModel.ts | 111 ++++++++++++++++++++++ src/components/form/core/useFormRef.ts | 34 +++++++ src/components/form/core/useFormSubmit.ts | 56 +++++++++++ 5 files changed, 256 insertions(+) create mode 100644 src/components/form/core/interface.ts create mode 100644 src/components/form/core/useFormItems.ts create mode 100644 src/components/form/core/useFormModel.ts create mode 100644 src/components/form/core/useFormRef.ts create mode 100644 src/components/form/core/useFormSubmit.ts diff --git a/src/components/form/core/interface.ts b/src/components/form/core/interface.ts new file mode 100644 index 0000000..3a949d6 --- /dev/null +++ b/src/components/form/core/interface.ts @@ -0,0 +1,9 @@ +import { InjectionKey } from "vue"; +import { FormRef } from "./useFormRef"; +import { FormSubmit } from "./useFormSubmit"; +import { FormItems } from "./useFormItems"; +import { FormModel } from "./useFormModel"; + +export type FormContextInterface = FormModel & FormItems & FormRef & FormSubmit; + +export const FormContext = Symbol("FormKey") as InjectionKey; diff --git a/src/components/form/core/useFormItems.ts b/src/components/form/core/useFormItems.ts new file mode 100644 index 0000000..fa4da7d --- /dev/null +++ b/src/components/form/core/useFormItems.ts @@ -0,0 +1,46 @@ +import { Ref } from "vue"; +import { FormItem } from "../hooks/types/FormItem"; + +export function useFormItems(items: Ref, model: Ref) { + const getItem = (field: string) => { + return items.value.find((i) => i.field === field); + }; + + const getItemOptions = (field: string) => { + const item = getItem(field); + if (item) { + return (item.nodeProps as any)?.options; + } + }; + + const initItemOptions = (field: string) => { + const item = getItem(field); + item && item.initial?.(); + }; + + const initItems = () => { + for (const item of items.value) { + item.initial?.(item, model); + } + }; + + const initItem = (field: string) => { + const item = getItem(field); + item && item.initial(item, model); + }; + + onMounted(() => { + initItems(); + }); + + return { + items, + getItem, + initItem, + initItems, + getItemOptions, + initItemOptions, + }; +} + +export type FormItems = ReturnType; diff --git a/src/components/form/core/useFormModel.ts b/src/components/form/core/useFormModel.ts new file mode 100644 index 0000000..9e137f0 --- /dev/null +++ b/src/components/form/core/useFormModel.ts @@ -0,0 +1,111 @@ +import { cloneDeep } from "lodash-es"; +import { Ref } from "vue"; + +/** + * 表单数据管理 + * @param initial 初始值 + * @returns + */ +export function useFormModel(model: Ref) { + const initial = cloneDeep(model.value); + + const resetModel = () => { + model.value = cloneDeep(initial); + }; + + const getInitialModel = () => { + return initial; + }; + + const setModel = (data: Recordable) => { + for (const key of Object.keys(model.value)) { + model.value[key] = data[key]; + } + }; + + const getModel = () => { + return formatModel(model.value); + }; + + return { + model, + getInitialModel, + resetModel, + setModel, + getModel, + }; +} + +export type FormModel = ReturnType; + +function formatModel(model: Recordable) { + const data: Recordable = {}; + + for (const [key, value] of Object.entries(model)) { + if (/^\[.+\]$/.test(key)) { + formatModelArray(key, value, data); + continue; + } + if (/^\{.+\}$/.test(key)) { + formatModelObject(key, value, data); + continue; + } + data[key] = value; + } + + return data; +} + +function formatModelArray(key: string, value: any, data: Recordable) { + let field = key.replaceAll(/\s/g, ""); + field = field.match(/^\[(.+)\]$/)?.[1] ?? ""; + + if (!field) { + data[key] = value; + return; + } + + const keys = field.split(","); + keys.forEach((k, i) => { + if (/(.+)?:number$/.test(k)) { + k = k.replace(/:number$/, ""); + data[k] = value?.[i] && Number(value[i]); + return; + } + if (/(.+)?:boolean$/.test(k)) { + k = k.replace(/:boolean$/, ""); + data[k] = value?.[i] && Boolean(value[i]); + return; + } + data[k] = value?.[i]; + }); + + return data; +} + +function formatModelObject(key: string, value: any, data: Recordable) { + let field = key.replaceAll(/\s/g, ""); + field = field.match(/^\{(.+)\}$/)?.[1] ?? ""; + + if (!field) { + data[key] = value; + return; + } + + const keys = field.split(","); + keys.forEach((k, i) => { + if (/(.+)?:number$/.test(k)) { + k = k.replace(/:number$/, ""); + data[k] = value?.[i] && Number(value[i]); + return; + } + if (/(.+)?:boolean$/.test(k)) { + k = k.replace(/:boolean$/, ""); + data[k] = value?.[i] && Boolean(value[i]); + return; + } + data[k] = value?.[i]; + }); + + return data; +} diff --git a/src/components/form/core/useFormRef.ts b/src/components/form/core/useFormRef.ts new file mode 100644 index 0000000..3aadc6c --- /dev/null +++ b/src/components/form/core/useFormRef.ts @@ -0,0 +1,34 @@ +import { FormInstance } from "@arco-design/web-vue"; + +export function useFormRef() { + /** + * 原始表单实例 + */ + const formRef = ref(null); + + type Validate = FormInstance["validate"]; + type ValidateField = FormInstance["validateField"]; + type ResetFields = FormInstance["resetFields"]; + type ClearValidate = FormInstance["clearValidate"]; + type SetFields = FormInstance["setFields"]; + type ScrollToField = FormInstance["scrollToField"]; + + const validate: Validate = async (...args) => formRef.value?.validate(...args); + const validateField: ValidateField = async (...args) => formRef.value?.validateField(...args); + const resetFields: ResetFields = (...args) => formRef.value?.resetFields(...args); + const clearValidate: ClearValidate = (...args) => formRef.value?.clearValidate(...args); + const setFields: SetFields = (...args) => formRef.value?.setFields(...args); + const scrollToField: ScrollToField = (...args) => formRef.value?.scrollToField(...args); + + return { + formRef, + validate, + validateField, + resetFields, + clearValidate, + setFields, + scrollToField, + }; +} + +export type FormRef = ReturnType; diff --git a/src/components/form/core/useFormSubmit.ts b/src/components/form/core/useFormSubmit.ts new file mode 100644 index 0000000..5fc68b6 --- /dev/null +++ b/src/components/form/core/useFormSubmit.ts @@ -0,0 +1,56 @@ +import { Ref } from "vue"; +import { FormItem } from "../hooks/types/FormItem"; +import { FormInstance, Message } from "@arco-design/web-vue"; + +interface Options { + items: Ref; + model: Ref; + submit: Ref; + validate: FormInstance["validate"]; +} + +export function useFormSubmit(options: Options) { + const { model, items, submit, validate } = options; + const loading = ref(false); + + /** + * 设置loading + * @param value 值 + */ + const setLoading = (value: boolean) => { + loading.value = value; + }; + + /** + * 提交表单 + */ + const submitForm = async () => { + if (await validate()) { + return; + } + try { + loading.value = true; + const res = await submit.value?.(model.value, items.value); + const msg = res?.data?.message; + msg && Message.success(`提示: ${msg}`); + } catch { + console.log(); + } finally { + loading.value = false; + } + }; + + /** + * 取消提交 + */ + const cancelForm = () => {}; + + return { + loading, + setLoading, + submitForm, + cancelForm, + }; +} + +export type FormSubmit = ReturnType;