feat: 添加表单钩子

master
luoer 2023-11-13 16:59:14 +08:00
parent a2c263cef7
commit 71baafecc7
5 changed files with 256 additions and 0 deletions

View File

@ -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<FormContextInterface>;

View File

@ -0,0 +1,46 @@
import { Ref } from "vue";
import { FormItem } from "../hooks/types/FormItem";
export function useFormItems(items: Ref<FormItem[]>, model: Ref<Recordable>) {
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<typeof useFormItems>;

View File

@ -0,0 +1,111 @@
import { cloneDeep } from "lodash-es";
import { Ref } from "vue";
/**
*
* @param initial
* @returns
*/
export function useFormModel(model: Ref<Recordable>) {
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<typeof useFormModel>;
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;
}

View File

@ -0,0 +1,34 @@
import { FormInstance } from "@arco-design/web-vue";
export function useFormRef() {
/**
*
*/
const formRef = ref<FormInstance | null>(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<typeof useFormRef>;

View File

@ -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<FormItem[]>;
model: Ref<Recordable>;
submit: Ref<Function | undefined>;
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<typeof useFormSubmit>;