@ithinkdt/core
Version:
iThinkDT Core
188 lines (159 loc) • 5.79 kB
JavaScript
import { computed, markRaw, readonly, reactive, shallowReactive, ref, isRef, unref, watch } from 'vue'
import { useI18n } from '../../i18n'
import { pageInit } from '../plugin'
import { isRequiredRule, required } from './rules/require.rule'
export * from './rules'
export function useForm(options) {
let { multiStep, items, model: _model = {}, initial, defaultSpan = 6, formRef = ref() } = options
const model = reactive(_model)
const reset = (_initial) => {
if (!_initial || _initial instanceof Event) {
_initial = { ..._model, ...initial }
}
initial = _initial
for (const k of [...Object.keys(model), ...Object.keys(_initial)]) {
model[k] = _initial[k] ?? undefined
}
}
if (initial) {
reset(initial)
}
if (Array.isArray(options) || typeof options === 'function') {
items = options
}
if (typeof items === 'function') {
const fi = (k, t, requiredOrOptions) => ({
...(typeof requiredOrOptions === 'boolean' || isRef(requiredOrOptions)
? { required: requiredOrOptions }
: requiredOrOptions),
name: k,
type: t,
})
const group = (show, items) => {
return items.map((it) => {
return {
...it,
hidden: computed(() => !unref(show) || unref(it.hidden) === true),
}
})
}
items = items({ model, formRef, reset, fi, group })
}
const { t, locale } = useI18n()
let _items
function _map(items) {
return items.map((it) => {
if (it.step) {
return reactive({
...it,
items: _map(it.items),
})
}
if (!it) {
return { hidden: true }
}
if (!it.props) {
it.props = {}
}
const rules = it.rule ? [it.rule].flat() : it.rule
if (it.name && !it.name.startsWith('$')) {
// eslint-disable-next-line unicorn/no-null
model[it.name] ??= null
}
let [_type, param] = typeof it.type === 'string' ? it.type.split('|') : []
const preset = (_type && pageInit.formPresets?.[_type]?.(it, param)) || {}
let type = preset.component || it.type
const _submit =
it.submit && preset._submit
? ($) => Promise.resolve(preset.submit($)).then(() => it.submit($))
: it.submit ?? preset.submit ?? preset._submit
return reactive({
...preset,
...it,
label: typeof it.label === 'function' ? computed(() => it.label(locale.value)) : it.label,
span: it.span ?? preset.span ?? defaultSpan,
type: type && typeof type === 'object' ? markRaw(type) : type,
props: {
...preset.props,
...it.props,
ref: ($) => {
it.$ = $
if (isRef(it.props?.ref)) {
it.props.ref.value = $
} else if (typeof it.props?.ref === 'function') {
it.props.ref($)
}
},
},
parse:
it.parse && preset._parse
? (v) => it.parse(preset._parse(v))
: it.parse ?? preset.parse ?? preset._parse,
transform:
it.transform && preset._transform
? (v) => preset.transform(it.transform(v))
: it.transform ?? preset.transform ?? preset._transform,
submit: () => _submit?.(it.$),
rule: [
rules?.find((r) => isRequiredRule(r))
? undefined
: required(
computed(() => t('form.validate.required', unref(it.label))),
{ required: it.required ?? false },
),
...(rules && preset._rule
? [...preset.rule, ...rules]
: rules ?? preset.rule ?? preset._rule ?? []),
].filter(Boolean),
})
})
}
if (isRef(items)) {
watch(items, (its) => {
if (!_items) return
_items = _map([...its])
updateIts()
})
}
const _its = shallowReactive([])
const step = ref(0)
const hasNext = computed(() =>
_items?.reduceRight((has, it, index) => {
if (index === step.value) return false
return has || !it.skip
}, false),
)
function updateIts() {
_its.length = 0
if (unref(multiStep)) {
_its.push(...(_items[step.value]?.items ?? []))
} else {
_its.push(..._items)
}
}
const init = () => {
_items = _map([...unref(items)])
updateIts()
}
pageInit.__$ ? init() : pageInit.$init.then(init)
return {
model,
formRef,
items: _its,
reset,
hasNext,
step: readonly(step),
next() {
while (_items[step.value + 1]?.skip) {
step.value = Math.min(step.value + 1, _items.length - 1)
}
updateIts()
},
prev() {
while (_items[step.value - 1]?.skip) {
step.value = Math.max(step.value - 1, 0)
}
updateIts()
},
}
}