feat: 添加表单钩子
parent
a2c263cef7
commit
71baafecc7
|
|
@ -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>;
|
||||
|
|
@ -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>;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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>;
|
||||
|
|
@ -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>;
|
||||
Loading…
Reference in New Issue