@modulab/form
Version:
表单
193 lines (159 loc) • 5.01 kB
text/typescript
import { useTemplateRef } from 'vue';
import { provide, inject, ref, useAttrs, watch } from 'vue';
export const FormContextKey = Symbol('formContext');
export const FormItemContextKey = Symbol('formItemContextKey');
export function useFormContext() {
const formRef = useTemplateRef('formRef');
// 内部状态
const formModel = ref({});
// 最终提交的数据
const formData = ref({});
// 维护联动回调
const fieldWatchers: Record<string, Array<(val: any) => void>> = {};
// 子组件方法表
const fieldMethods: Record<string, any> = {};
function update(key: string, value: any) {
formModel.value[key] = value;
if (fieldWatchers[key]) {
fieldWatchers[key].forEach((cb) => cb(value));
}
}
function watchField(key: string, cb: (val: any) => void) {
if (!fieldWatchers[key]) {
fieldWatchers[key] = [];
}
fieldWatchers[key].push(cb);
return () => {
fieldWatchers[key] = fieldWatchers[key].filter((fn) => fn !== cb);
};
}
function registerFieldMethods(key: string, methods: Record<string, any>) {
fieldMethods[key] = methods;
}
function callFieldMethod(key: string, methodName: string, ...args: any[]) {
if (fieldMethods[key]?.[methodName]) {
return fieldMethods[key][methodName](...args);
}
}
function updateFormData(key: string | string[], value: any, split = true) {
const keys = Array.isArray(key) ? [...key] : [key];
const values = Array.isArray(value) ? value : [value];
keys.forEach((key, key_idx) => {
formData.value[key] = split ? values[key_idx] : value;
});
}
function backfill(data: Record<any, any>) {
Object.keys(data).forEach((key) => {
callFieldMethod(key, 'setValue', data[key]);
});
}
async function validate() {
return await formRef.value.validate();
}
const context = {
formRef,
formModel,
formData,
update,
updateFormData,
validate,
watchField,
registerFieldMethods,
callFieldMethod,
backfill,
};
provide(FormContextKey, context);
return context;
}
export function useFormItemContext(props) {
const visible = ref(props.visible);
function setVisible(val: boolean) {
visible.value = val;
}
const context = {
visible,
setVisible,
};
provide(FormItemContextKey, context);
return context;
}
export function useFormField(options: Record<string, any>, split = true) {
const attrs = useAttrs();
const { prop, defaultValue } = options;
// props 是复数用于多字段
const { fields } = attrs;
const value = ref(defaultValue);
const { update, updateFormData, registerFieldMethods } =
inject(FormContextKey);
const { setVisible } = inject(FormItemContextKey);
update(prop, defaultValue);
updateFormData(
fields ? fields : prop,
fields ? [defaultValue, defaultValue] : defaultValue,
split,
);
function setValue(val: any) {
value.value = val;
}
registerFieldMethods(prop, {
setValue,
});
watch(value, (val) => {
update(prop, val);
if (val === null) {
updateFormData(
fields ? fields : prop,
fields ? [val, val] : val,
split,
);
} else {
updateFormData(fields ? fields : prop, val, split);
}
});
return {
value,
update,
setValue,
setVisible,
registerFieldMethods,
};
}
export function useCurrentOptions(
options: any[] | (() => Promise<any[]> | any[]),
) {
const currentOptions = ref<any[]>([]);
async function setOptions(source: typeof options) {
try {
if (typeof source === 'function') {
const result = await source();
currentOptions.value = Array.isArray(result) ? result : [];
} else {
currentOptions.value = source ?? [];
}
} catch (e) {
console.error(e);
}
}
watch(
() => options,
async (newOptions) => {
await setOptions(newOptions);
},
{ immediate: true },
);
return { currentOptions, setOptions, reload: () => setOptions(options) };
}
export function useDependency(value, methods) {
const attrs = useAttrs();
const { watchField, formModel } = inject(FormContextKey);
const { dependsOn, onDependency } = attrs;
// 如果声明了依赖
if (dependsOn && onDependency) {
const keys = Array.isArray(dependsOn) ? dependsOn : [dependsOn];
keys.forEach((k) => {
watchField(k, (val) => {
onDependency(val, methods, formModel.value, value);
});
});
}
}