mirror of
https://github.com/fugary/simple-element-plus-template.git
synced 2025-11-12 14:27:49 +00:00
优化表单赋值与验证
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||||
import { debounce, isObject } from 'lodash'
|
import { debounce, isEmpty, isObject } from 'lodash'
|
||||||
import { onClickOutside, onKeyStroke, useVModel } from '@vueuse/core'
|
import { onClickOutside, onKeyStroke, useVModel } from '@vueuse/core'
|
||||||
import chunk from 'lodash/chunk'
|
import chunk from 'lodash/chunk'
|
||||||
import { UPDATE_MODEL_EVENT, CHANGE_EVENT, useFormItem } from 'element-plus'
|
import { UPDATE_MODEL_EVENT, CHANGE_EVENT, useFormItem } from 'element-plus'
|
||||||
@@ -183,7 +183,7 @@ const onInputKeywords = debounce((input) => {
|
|||||||
}, props.debounceTime)
|
}, props.debounceTime)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
onClickOutside(autocompletePopover.value?.popperRef?.contentRef, (event) => {
|
onClickOutside(autocompletePopover.value?.popperRef?.contentRef, () => {
|
||||||
popoverVisible.value = false
|
popoverVisible.value = false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -202,6 +202,9 @@ watch(() => props.modelValue, (value) => {
|
|||||||
console.info('=====================value', value)
|
console.info('=====================value', value)
|
||||||
if (!props.useIdModel) {
|
if (!props.useIdModel) {
|
||||||
setAutocompleteLabel(value && isObject(value) ? value[labelProp.value] : '')
|
setAutocompleteLabel(value && isObject(value) ? value[labelProp.value] : '')
|
||||||
|
if (isEmpty(value)) {
|
||||||
|
vModel.value = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -277,11 +280,11 @@ const moveSelection = function (down) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 向下按键移动元素
|
// 向下按键移动元素
|
||||||
onKeyStroke('ArrowDown', e => moveSelection(true))
|
onKeyStroke('ArrowDown', () => moveSelection(true))
|
||||||
// 向上按键移动元素
|
// 向上按键移动元素
|
||||||
onKeyStroke('ArrowUp', e => moveSelection(false))
|
onKeyStroke('ArrowUp', () => moveSelection(false))
|
||||||
// 选中回车
|
// 选中回车
|
||||||
onKeyStroke('Enter', e => {
|
onKeyStroke('Enter', () => {
|
||||||
onSelectData(currentOnRow.value)
|
onSelectData(currentOnRow.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { $i18nBundle } from '@/messages'
|
import { $i18nBundle } from '@/messages'
|
||||||
import ControlChild from '@/components/common-form-control/control-child.vue'
|
import ControlChild from '@/components/common-form-control/control-child.vue'
|
||||||
import { useInputType } from '@/components/utils'
|
import { useInputType } from '@/components/utils'
|
||||||
|
import cloneDeep from 'lodash/cloneDeep'
|
||||||
|
import { get, set } from 'lodash'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{option:CommonFormOption}}
|
* @type {{option:CommonFormOption}}
|
||||||
@@ -16,17 +18,19 @@ const props = defineProps({
|
|||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
type: Object
|
type: Object,
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const inputType = computed(() => useInputType(props.option))
|
const inputType = computed(() => useInputType(props.option))
|
||||||
|
|
||||||
const modelAttrs = computed(() => {
|
const modelAttrs = computed(() => {
|
||||||
if (['el-input', 'el-select', 'common-autocomplete', 'el-autocomplete', 'el-cascader', 'el-tree-select'].includes(inputType.value)) {
|
const attrs = { ...props.option.attrs }
|
||||||
return Object.assign({ clearable: true }, props.option.attrs || {})
|
if (attrs.clearable === undefined && ['el-input', 'el-select', 'common-autocomplete', 'el-autocomplete', 'el-cascader', 'el-tree-select'].includes(inputType.value)) {
|
||||||
|
attrs.clearable = true
|
||||||
}
|
}
|
||||||
return props.option.attrs
|
return attrs
|
||||||
})
|
})
|
||||||
|
|
||||||
const label = computed(() => {
|
const label = computed(() => {
|
||||||
@@ -42,13 +46,13 @@ const formModel = computed(() => props.option.model || props.model)
|
|||||||
const modelValue = computed({
|
const modelValue = computed({
|
||||||
get () {
|
get () {
|
||||||
if (formModel.value && props.option.prop) {
|
if (formModel.value && props.option.prop) {
|
||||||
return formModel.value[props.option.prop]
|
return get(formModel.value, props.option.prop)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
set (val) {
|
set (val) {
|
||||||
if (formModel.value && props.option.prop) {
|
if (formModel.value && props.option.prop) {
|
||||||
formModel.value[props.option.prop] = val
|
set(formModel.value, props.option.prop, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -70,10 +74,38 @@ const children = computed(() => {
|
|||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const formItemRef = ref()
|
||||||
|
|
||||||
|
const rules = computed(() => {
|
||||||
|
const option = props.option
|
||||||
|
let _rules = cloneDeep(option.rules || [])
|
||||||
|
if (option.prop) {
|
||||||
|
if (option.required !== undefined) {
|
||||||
|
const label = option.label || $i18nBundle(option.labelKey)
|
||||||
|
_rules = [{
|
||||||
|
trigger: option.trigger,
|
||||||
|
required: option.required,
|
||||||
|
message: $i18nBundle('common.msg.nonNull', [label])
|
||||||
|
}, ..._rules]
|
||||||
|
}
|
||||||
|
if (option.pattern !== undefined) {
|
||||||
|
const label = option.label || $i18nBundle(option.labelKey)
|
||||||
|
_rules = [{
|
||||||
|
pattern: option.pattern,
|
||||||
|
message: option.patternMsg || $i18nBundle('common.msg.patternInvalid', [label])
|
||||||
|
}, ..._rules]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formItemRef.value && formItemRef.value.clearValidate()
|
||||||
|
return _rules
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
|
ref="formItemRef"
|
||||||
|
:rules="rules"
|
||||||
:prop="option.prop"
|
:prop="option.prop"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import cloneDeep from 'lodash/cloneDeep'
|
|
||||||
import { $i18nBundle } from '@/messages'
|
|
||||||
import { useVModel } from '@vueuse/core'
|
import { useVModel } from '@vueuse/core'
|
||||||
|
import { set } from 'lodash'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {CommonFormProps}
|
* @type {CommonFormProps}
|
||||||
@@ -65,35 +64,6 @@ const props = defineProps({
|
|||||||
|
|
||||||
const form = ref()
|
const form = ref()
|
||||||
|
|
||||||
const rules = computed(() => {
|
|
||||||
const ruleResult = {}
|
|
||||||
props.options.forEach(option => {
|
|
||||||
if (option.prop) {
|
|
||||||
let _rules = cloneDeep(option.rules || [])
|
|
||||||
if (option.required !== undefined) {
|
|
||||||
const label = option.label || $i18nBundle(option.labelKey)
|
|
||||||
_rules = [{
|
|
||||||
trigger: option.trigger,
|
|
||||||
required: option.required,
|
|
||||||
message: $i18nBundle('common.msg.nonNull', [label])
|
|
||||||
}, ..._rules]
|
|
||||||
}
|
|
||||||
if (option.pattern !== undefined) {
|
|
||||||
const label = option.label || $i18nBundle(option.labelKey)
|
|
||||||
_rules = [{
|
|
||||||
pattern: option.pattern,
|
|
||||||
message: option.patternMsg || $i18nBundle('common.msg.patternInvalid', [label])
|
|
||||||
}, ..._rules]
|
|
||||||
}
|
|
||||||
if (_rules.length) {
|
|
||||||
ruleResult[option.prop] = _rules
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
form.value && form.value.clearValidate()
|
|
||||||
return ruleResult
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['submitForm', 'update:model'])
|
const emit = defineEmits(['submitForm', 'update:model'])
|
||||||
|
|
||||||
const formModel = useVModel(props, 'model', emit)
|
const formModel = useVModel(props, 'model', emit)
|
||||||
@@ -102,7 +72,7 @@ const initFormModel = () => {
|
|||||||
if (formModel.value) {
|
if (formModel.value) {
|
||||||
props.options.forEach(option => {
|
props.options.forEach(option => {
|
||||||
if (option.prop) {
|
if (option.prop) {
|
||||||
formModel.value[option.prop] = option.value || undefined
|
set(formModel.value, option.prop, option.value || undefined)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -123,19 +93,29 @@ defineExpose({
|
|||||||
ref="form"
|
ref="form"
|
||||||
class="common-form"
|
class="common-form"
|
||||||
:model="formModel"
|
:model="formModel"
|
||||||
:rules="rules"
|
|
||||||
:label-width="labelWidth"
|
:label-width="labelWidth"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:validate-on-rule-change="validateOnRuleChange"
|
:validate-on-rule-change="validateOnRuleChange"
|
||||||
>
|
>
|
||||||
<common-form-control
|
<template
|
||||||
v-for="(option,index) in options"
|
v-for="(option,index) in options"
|
||||||
:key="index"
|
:key="index"
|
||||||
:model="formModel"
|
>
|
||||||
:option="option"
|
<slot
|
||||||
/>
|
v-if="option.slot"
|
||||||
|
name="option.slot"
|
||||||
|
:option="option"
|
||||||
|
:form="form"
|
||||||
|
:model="formModel"
|
||||||
|
/>
|
||||||
|
<common-form-control
|
||||||
|
:model="formModel"
|
||||||
|
:option="option"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<slot
|
<slot
|
||||||
:form="form"
|
:form="form"
|
||||||
|
:model="formModel"
|
||||||
name="default"
|
name="default"
|
||||||
/>
|
/>
|
||||||
<el-form-item v-if="showButtons">
|
<el-form-item v-if="showButtons">
|
||||||
@@ -160,11 +140,13 @@ defineExpose({
|
|||||||
</el-button>
|
</el-button>
|
||||||
<slot
|
<slot
|
||||||
:form="form"
|
:form="form"
|
||||||
|
:model="formModel"
|
||||||
name="buttons"
|
name="buttons"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<slot
|
<slot
|
||||||
:form="form"
|
:form="form"
|
||||||
|
:model="formModel"
|
||||||
name="after-buttons"
|
name="after-buttons"
|
||||||
/>
|
/>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|||||||
2
src/components/common-form/public.d.ts
vendored
2
src/components/common-form/public.d.ts
vendored
@@ -41,6 +41,8 @@ export interface CommonFormOption {
|
|||||||
tooltip: string;
|
tooltip: string;
|
||||||
/** 提示函数 */
|
/** 提示函数 */
|
||||||
tooltipFunc: () => void;
|
tooltipFunc: () => void;
|
||||||
|
/** 自定义slot名称 */
|
||||||
|
slot: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommonFormProps extends FormProps {
|
export interface CommonFormProps extends FormProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user