ivue-material-plus
Version:
A high quality UI components Library with Vue.js
1 lines • 9.88 kB
Source Map (JSON)
{"version":3,"file":"form.mjs","sources":["../../../../../src/components/ivue-form/form.vue"],"sourcesContent":["<template>\r\n <form :class=\"wrapperClasses\" @submit.prevent>\r\n <slot></slot>\r\n </form>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport {\r\n computed,\r\n defineComponent,\r\n provide,\r\n toRefs,\r\n reactive,\r\n ref,\r\n watch,\r\n} from 'vue';\r\nimport { oneOf } from '../../utils/assist';\r\nimport { isFunction } from '../../utils/helpers';\r\nimport { debugWarn } from '../../utils/error';\r\nimport { filterFields, useFormLabelWidth } from './utils';\r\n\r\n// type\r\nimport {\r\n Props,\r\n FormContext,\r\n FormContextKey,\r\n FormValidateCallback,\r\n FormValidationResult,\r\n} from './types/form';\r\nimport type {\r\n FormItemProp,\r\n Arrayable,\r\n FormItemContext,\r\n} from './types/form-item';\r\nimport type { ValidateFieldsError } from 'async-validator';\r\n\r\nconst prefixCls = 'ivue-form';\r\n\r\nexport default defineComponent({\r\n name: prefixCls,\r\n props: {\r\n /**\r\n * 表单域标签的位置\r\n *\r\n * @type {String}\r\n */\r\n labelPosition: {\r\n type: String,\r\n validator(value: string) {\r\n return oneOf(value, ['left', 'right', 'top']);\r\n },\r\n default: 'right',\r\n },\r\n /**\r\n * 错误星号的位置\r\n *\r\n * @type {String}\r\n */\r\n requireAsteriskPosition: {\r\n type: String,\r\n validator(value: string) {\r\n return oneOf(value, ['left', 'right']);\r\n },\r\n default: 'left',\r\n },\r\n /**\r\n * 表单域标签的后缀\r\n *\r\n * @type {String}\r\n */\r\n labelSuffix: {\r\n type: String,\r\n default: '',\r\n },\r\n /**\r\n * 标签宽度\r\n *\r\n * @type {String | Number}\r\n */\r\n labelWidth: {\r\n type: [String, Number],\r\n default: '',\r\n },\r\n /**\r\n * 行内表单模式\r\n *\r\n * @type {Boolean}\r\n */\r\n inline: {\r\n type: Boolean,\r\n },\r\n /**\r\n * 表单数据对象\r\n *\r\n * @type {Object}\r\n */\r\n model: {\r\n type: Object,\r\n },\r\n /**\r\n * 表单验证规则\r\n *\r\n * @type {Object}\r\n */\r\n rules: {\r\n type: Object,\r\n },\r\n /**\r\n * 是否显示校验错误信息\r\n *\r\n * @type {Boolean}\r\n */\r\n showMessage: {\r\n type: Boolean,\r\n default: true,\r\n },\r\n /**\r\n * 验证成功提示状态\r\n *\r\n * @type {Boolean}\r\n */\r\n showSuccessStatus: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 是否隐藏必填字段标签旁边的红色星号\r\n *\r\n * @type {Boolean}\r\n */\r\n hideRequiredAsterisk: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 是否在 rules 属性改变后立即触发一次验证\r\n *\r\n * @type {Boolean}\r\n */\r\n validateOnRuleChange: {\r\n type: Boolean,\r\n default: true,\r\n },\r\n /**\r\n * 是否禁用该表单内的所有组件\r\n *\r\n * @type {Boolean}\r\n */\r\n disabled: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 当校验失败时,滚动到第一个错误表单项\r\n *\r\n * @type {Boolean}\r\n */\r\n scrollToError: {\r\n type: Boolean,\r\n },\r\n },\r\n setup(props: Props, { emit }) {\r\n // 验证的字段\r\n const fields = ref<FormItemContext[]>([]);\r\n\r\n // computed\r\n\r\n // 样式\r\n const wrapperClasses = computed(() => [\r\n prefixCls,\r\n {\r\n // 表单域标签的位置\r\n [`${prefixCls}-label-${props.labelPosition}`]: props.labelPosition,\r\n // 行内表单模式\r\n [`${prefixCls}-inline`]: props.inline,\r\n },\r\n ]);\r\n\r\n // 是否可以验证\r\n const isValidatable = computed(() => {\r\n const hasModel = !!props.model;\r\n\r\n if (!hasModel) {\r\n debugWarn(prefixCls, 'model is required for validate to work.');\r\n }\r\n\r\n return hasModel;\r\n });\r\n\r\n // methods\r\n\r\n // 对整个表单的内容进行验证\r\n const validate = async (\r\n callback?: FormValidateCallback\r\n ): FormValidationResult => validateField(undefined, callback);\r\n\r\n // 验证具体的某个字段\r\n const validateField: FormContext['validateField'] = async (\r\n modelProps = [],\r\n callback\r\n ) => {\r\n // 是否是函数\r\n const shouldThrow = !isFunction(callback);\r\n\r\n try {\r\n // 验证字段\r\n const result = await useValidateField(modelProps);\r\n\r\n // 当结果为 false 时意味着字段不可验证\r\n if (result === true) {\r\n callback?.(result);\r\n }\r\n\r\n return result;\r\n } catch (error) {\r\n const invalidFields = error as ValidateFieldsError;\r\n\r\n // 当校验失败时,滚动到第一个错误表单项\r\n if (props.scrollToError) {\r\n scrollToField(Object.keys(invalidFields)[0]);\r\n }\r\n\r\n // 返回失败\r\n callback?.(false, invalidFields);\r\n\r\n return shouldThrow && Promise.reject(invalidFields);\r\n }\r\n };\r\n\r\n // 验证字段\r\n const useValidateField = async (props: Arrayable<FormItemProp> = []) => {\r\n // 是否可以验证\r\n if (!isValidatable.value) {\r\n return false;\r\n }\r\n\r\n // 获取验证字段\r\n fields.value = getValidateFields(props);\r\n\r\n // 都通过验证\r\n if (fields.value.length === 0) {\r\n return true;\r\n }\r\n\r\n // 验证错误的字段\r\n let validationErrors: ValidateFieldsError = {};\r\n\r\n // 循环触发item的验证\r\n for (const field of fields.value) {\r\n try {\r\n // item验证字段\r\n await field.validate('');\r\n } catch (error) {\r\n // 验证错误的字段\r\n validationErrors = {\r\n ...validationErrors,\r\n ...(error as ValidateFieldsError),\r\n };\r\n }\r\n }\r\n\r\n // 有验证错误的字段\r\n if (Object.keys(validationErrors).length === 0) {\r\n return true;\r\n }\r\n\r\n return Promise.reject(validationErrors);\r\n };\r\n\r\n // 获取验证字段\r\n const getValidateFields = (props: Arrayable<FormItemProp>) => {\r\n if (fields.value.length === 0) {\r\n return [];\r\n }\r\n\r\n // 过滤字段\r\n const filteredFields = filterFields(fields.value, props);\r\n\r\n if (!filteredFields.length) {\r\n debugWarn(prefixCls, 'please pass correct props!');\r\n\r\n return [];\r\n }\r\n\r\n return filteredFields;\r\n };\r\n\r\n // 添加验证字段\r\n const addField: FormContext['addField'] = (field) => {\r\n fields.value.push(field);\r\n };\r\n\r\n // 删除验证字段\r\n const removeField: FormContext['removeField'] = (field) => {\r\n if (field.prop) {\r\n fields.value.splice(fields.value.indexOf(field), 1);\r\n }\r\n };\r\n\r\n // 滚动到指定的字段\r\n const scrollToField = (prop: FormItemProp) => {\r\n // 当校验失败时,滚动到第一个错误表单项\r\n const field = filterFields(fields.value, prop)[0];\r\n\r\n if (field) {\r\n field.$el?.scrollIntoView();\r\n }\r\n };\r\n\r\n // 重置该表单项\r\n const resetFields: FormContext['resetFields'] = (properties = []) => {\r\n // 表单数据对象\r\n if (!props.model) {\r\n debugWarn(prefixCls, 'model is required for resetFields to work.');\r\n return;\r\n }\r\n\r\n // 过滤字段\r\n filterFields(fields.value, properties).forEach((field) =>\r\n field.resetField()\r\n );\r\n };\r\n\r\n // 清理某个字段的表单验证信息\r\n const clearValidate: FormContext['clearValidate'] = (props = []) => {\r\n // 获取需要清理的item\r\n filterFields(fields.value, props).forEach((field) =>\r\n field.clearValidate()\r\n );\r\n };\r\n\r\n // watch\r\n\r\n // 监听 rules 属性改变后立即触发一次验证\r\n watch(\r\n () => props.rules,\r\n () => {\r\n if (props.validateOnRuleChange) {\r\n validate().catch((err) => debugWarn(err));\r\n }\r\n }\r\n );\r\n\r\n // provide\r\n provide(\r\n FormContextKey,\r\n reactive({\r\n // props\r\n ...toRefs(props),\r\n // emit\r\n emit,\r\n // 添加验证字段\r\n addField,\r\n // 删除验证字段\r\n removeField,\r\n // 清理某个字段的表单验证信息\r\n clearValidate,\r\n // 重置该表单项\r\n resetFields,\r\n // 验证具体的某个字段\r\n validateField,\r\n\r\n // 表单宽度\r\n ...useFormLabelWidth(),\r\n })\r\n );\r\n\r\n return {\r\n // computed\r\n wrapperClasses,\r\n\r\n // methods\r\n validate,\r\n validateField,\r\n resetFields,\r\n scrollToField,\r\n clearValidate,\r\n };\r\n },\r\n});\r\n</script>\r\n"],"names":["_openBlock","_createElementBlock","_normalizeClass","_withModifiers"],"mappings":";;;;AACc,SAAA,YAAA,IAAE,EAAA,MAAA,EAAc,MAAA,EAAA,MAAA,EAAA,OAAA,QAAA,EAAA;EAAG,OAAAA,SAAA,EAAM,EAAAC,kBAAA,CAAA,MAAA,EAAA;AAAA,IAAA,KAAA,EAAAC,cAAA,CAAA,IAAA,CAAA,cAAA,CAAA;AAAA,IACnC,UAAa,MAAA,CAAA,CAAA,CAAA,KAAA,MAAA,CAAA,CAAA,CAAA,GAAAC,cAAA,MAAA;AAAA,KAAA,EAAA,CAAA,SAAA,CAAA,CAAA,CAAA;AAAA,GAAA,EAAA;AAAA;;;;;;;"}