forked from mengyxu/noob-components
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
3.4 KiB
132 lines
3.4 KiB
import { reactive, Ref, toRaw } from "vue"; |
|
import {} from "noob-mengyxu"; |
|
import * as Element from "../element"; |
|
import { clearAndAssign, deepCopy } from "../util/objectUtil"; |
|
|
|
const { showMessage } = Element; |
|
|
|
export interface FormProp { |
|
code: string; |
|
name?: string; |
|
i18n?: string; |
|
dict?: string; |
|
slot?: boolean; |
|
|
|
[others: string]: any; |
|
} |
|
|
|
interface Options { |
|
props?: FormProp[]; |
|
formRef?: Ref<any>; |
|
handleAdd?: (value) => Promise<void>; |
|
handleEdit?: (value) => Promise<void>; |
|
handleCancel?: () => Promise<void>; |
|
handleError?: (kind: "validation" | "internal", err?) => void; |
|
init?: any; |
|
extraProps?: string[] | "any" | ((code: string) => boolean); |
|
} |
|
|
|
export function useModifyForm(options: Options) { |
|
const { formRef, handleAdd, handleEdit, handleCancel, init, extraProps } = options; |
|
const initValue = init ?? {}; |
|
const props = options.props ?? []; |
|
const propMap = Object.fromEntries(props.map((prop) => [prop.code, prop])); |
|
|
|
let isValidProp: (code: string) => boolean; |
|
|
|
if (typeof extraProps === "function") { |
|
isValidProp = (code: string) => extraProps(code) as boolean; |
|
} else if (Array.isArray(extraProps)) { |
|
isValidProp = (code: string) => extraProps.includes(code); |
|
} else { |
|
isValidProp = (_code: string) => extraProps === "any"; |
|
} |
|
|
|
const normalizeValue = (code: string, value: any) => { |
|
const prop = propMap[code]; |
|
if (!prop && !isValidProp(code)) return null; |
|
if (prop?.dict) { |
|
const parsed = parseInt(value); |
|
return isNaN(parsed) ? value : parsed; |
|
} |
|
|
|
return value; |
|
}; |
|
|
|
const normalizeModel = (newModel: Record<string, any>) => |
|
Object.fromEntries( |
|
Object.entries(newModel).flatMap(([code, value]) => { |
|
const normalized = normalizeValue(code, value); |
|
return normalized != null ? [[code, normalized]] : []; |
|
}) |
|
); |
|
|
|
const handleError = |
|
options.handleError ?? |
|
((kind, err) => { |
|
if (kind === "validation") { |
|
showMessage("error", "Validation Error"); |
|
} else if (kind === "internal") { |
|
const msg = `Internal Error: ${err}`; |
|
showMessage("error", msg); |
|
console.error(msg); |
|
} |
|
}); |
|
|
|
return { |
|
flagOnClose: null, |
|
flagOnAdd: "add", |
|
flagOnEdit: "edit", |
|
|
|
data: reactive<{ dialog: any; model: Record<string, any> }>({ |
|
dialog: null, |
|
model: { ...initValue }, |
|
}), |
|
|
|
clearModel() { |
|
clearAndAssign(this.data.model, initValue); |
|
}, |
|
|
|
assignModel(newModel: Record<string, any>) { |
|
const normalized = normalizeModel(newModel); |
|
this.clearModel(); |
|
Object.assign(this.data.model, normalized); |
|
}, |
|
|
|
openAdd() { |
|
this.clearModel(); |
|
formRef?.value?.clearValidate(); |
|
this.data.dialog = this.flagOnAdd; |
|
}, |
|
|
|
openEdit(toEdit: Record<string, any>) { |
|
this.assignModel(toEdit); |
|
formRef?.value?.clearValidate(); |
|
this.data.dialog = this.flagOnEdit; |
|
}, |
|
|
|
close() { |
|
this.data.dialog = this.flagOnClose; |
|
}, |
|
|
|
async onConfirm() { |
|
const value = deepCopy(toRaw(this.data.model)); |
|
try { |
|
if (this.data.dialog === this.flagOnAdd) { |
|
await handleAdd?.(value); |
|
} else if (this.data.dialog === this.flagOnEdit) { |
|
await handleEdit?.(value); |
|
} |
|
} catch (err) { |
|
handleError?.("internal", err); |
|
} |
|
|
|
this.close(); |
|
}, |
|
|
|
async onCancel() { |
|
await handleCancel?.(); |
|
this.close(); |
|
}, |
|
}; |
|
}
|
|
|