|
|
|
@ -1,166 +1,143 @@
|
|
|
|
|
<!-- |
|
|
|
|
* @Description: |
|
|
|
|
--> |
|
|
|
|
<script lang="ts"> |
|
|
|
|
<script lang="ts" setup> |
|
|
|
|
import type { PropType } from 'vue' |
|
|
|
|
import { computed, defineComponent, reactive, toRefs, unref } from 'vue' |
|
|
|
|
import { computed, unref } from 'vue' |
|
|
|
|
import { asyncComputed } from '@vueuse/core' |
|
|
|
|
import { omit } from 'lodash-es' |
|
|
|
|
import { Col, Divider, FormItem, Tooltip } from 'ant-design-vue' |
|
|
|
|
import { componentMap } from '../../core/formItemConfig' |
|
|
|
|
import type { IFormConfig, IVFormComponent } from '../../typings/v-form-component' |
|
|
|
|
import { handleAsyncOptions } from '../../utils' |
|
|
|
|
|
|
|
|
|
// import FormItem from '/@/components/Form/src/components/FormItem.vue'; |
|
|
|
|
|
|
|
|
|
import { useFormModelState } from '../../hooks/useFormDesignState' |
|
|
|
|
import { Icon } from '@/components/Icon' |
|
|
|
|
|
|
|
|
|
export default defineComponent({ |
|
|
|
|
name: 'VFormItem', |
|
|
|
|
components: { |
|
|
|
|
Tooltip, |
|
|
|
|
Icon, |
|
|
|
|
FormItem, |
|
|
|
|
Divider, |
|
|
|
|
Col, |
|
|
|
|
const props = defineProps({ |
|
|
|
|
formData: { |
|
|
|
|
type: Object, |
|
|
|
|
default: () => ({}), |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
props: { |
|
|
|
|
formData: { |
|
|
|
|
type: Object, |
|
|
|
|
default: () => ({}), |
|
|
|
|
}, |
|
|
|
|
schema: { |
|
|
|
|
type: Object as PropType<IVFormComponent>, |
|
|
|
|
required: true, |
|
|
|
|
}, |
|
|
|
|
formConfig: { |
|
|
|
|
type: Object as PropType<IFormConfig>, |
|
|
|
|
required: true, |
|
|
|
|
}, |
|
|
|
|
schema: { |
|
|
|
|
type: Object as PropType<IVFormComponent>, |
|
|
|
|
required: true, |
|
|
|
|
}, |
|
|
|
|
emits: ['update:form-data', 'change'], |
|
|
|
|
setup(props, { emit }) { |
|
|
|
|
const state = reactive({ |
|
|
|
|
componentMap, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
const { formModel: formData1, setFormModel } = useFormModelState() |
|
|
|
|
const colPropsComputed = computed(() => { |
|
|
|
|
const { colProps = {} } = props.schema |
|
|
|
|
return colProps |
|
|
|
|
}) |
|
|
|
|
const formItemProps = computed(() => { |
|
|
|
|
const { formConfig } = unref(props) |
|
|
|
|
let { field, required, rules, labelCol, wrapperCol } = unref(props.schema) |
|
|
|
|
const { colon } = props.formConfig |
|
|
|
|
|
|
|
|
|
const { itemProps } = unref(props.schema) |
|
|
|
|
|
|
|
|
|
// <editor-fold desc="布局属性"> |
|
|
|
|
labelCol = labelCol || (formConfig.layout === 'horizontal' |
|
|
|
|
? formConfig.labelLayout === 'flex' |
|
|
|
|
? { style: `width:${formConfig.labelWidth}px` } |
|
|
|
|
: formConfig.labelCol |
|
|
|
|
: {}) |
|
|
|
|
|
|
|
|
|
wrapperCol = wrapperCol || (formConfig.layout === 'horizontal' |
|
|
|
|
? formConfig.labelLayout === 'flex' |
|
|
|
|
? { style: 'width:auto;flex:1' } |
|
|
|
|
: formConfig.wrapperCol |
|
|
|
|
: {}) |
|
|
|
|
|
|
|
|
|
const style = formConfig.layout === 'horizontal' && formConfig.labelLayout === 'flex' ? { display: 'flex' } : {} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
formConfig: { |
|
|
|
|
type: Object as PropType<IFormConfig>, |
|
|
|
|
required: true, |
|
|
|
|
}, |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
const emit = defineEmits(['update:form-data', 'change']) |
|
|
|
|
|
|
|
|
|
const { formModel: formData1, setFormModel } = useFormModelState() |
|
|
|
|
const colPropsComputed = computed(() => { |
|
|
|
|
const { colProps = {} } = props.schema |
|
|
|
|
return colProps |
|
|
|
|
}) |
|
|
|
|
const formItemProps = computed(() => { |
|
|
|
|
const { formConfig } = unref(props) |
|
|
|
|
let { field, required, rules, labelCol, wrapperCol } = unref(props.schema) |
|
|
|
|
const { colon } = props.formConfig |
|
|
|
|
|
|
|
|
|
const { itemProps } = unref(props.schema) |
|
|
|
|
|
|
|
|
|
// <editor-fold desc="布局属性"> |
|
|
|
|
labelCol = labelCol || (formConfig.layout === 'horizontal' |
|
|
|
|
? formConfig.labelLayout === 'flex' |
|
|
|
|
? { style: `width:${formConfig.labelWidth}px` } |
|
|
|
|
: formConfig.labelCol |
|
|
|
|
: {}) |
|
|
|
|
|
|
|
|
|
wrapperCol = wrapperCol || (formConfig.layout === 'horizontal' |
|
|
|
|
? formConfig.labelLayout === 'flex' |
|
|
|
|
? { style: 'width:auto;flex:1' } |
|
|
|
|
: formConfig.wrapperCol |
|
|
|
|
: {}) |
|
|
|
|
|
|
|
|
|
const style |
|
|
|
|
= formConfig.layout === 'horizontal' && formConfig.labelLayout === 'flex' |
|
|
|
|
? { display: 'flex' } |
|
|
|
|
: {} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 将字符串正则格式化成正则表达式 |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
const newConfig = Object.assign( |
|
|
|
|
{}, |
|
|
|
|
{ |
|
|
|
|
name: field, |
|
|
|
|
style: { ...style }, |
|
|
|
|
colon, |
|
|
|
|
required, |
|
|
|
|
rules, |
|
|
|
|
labelCol, |
|
|
|
|
wrapperCol, |
|
|
|
|
}, |
|
|
|
|
itemProps, |
|
|
|
|
) |
|
|
|
|
if (!itemProps?.labelCol?.span) |
|
|
|
|
newConfig.labelCol = labelCol |
|
|
|
|
|
|
|
|
|
if (!itemProps?.wrapperCol?.span) |
|
|
|
|
newConfig.wrapperCol = wrapperCol |
|
|
|
|
|
|
|
|
|
if (!itemProps?.rules) |
|
|
|
|
newConfig.rules = rules |
|
|
|
|
|
|
|
|
|
return newConfig |
|
|
|
|
}) as Recordable |
|
|
|
|
|
|
|
|
|
const componentItem = computed(() => componentMap.get(props.schema.component as string)) |
|
|
|
|
|
|
|
|
|
// console.log('component change:', props.schema.component, componentItem.value); |
|
|
|
|
const handleClick = (schema: IVFormComponent) => { |
|
|
|
|
if (schema.component === 'Button' && schema.componentProps?.handle) |
|
|
|
|
emit(schema.componentProps?.handle) |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
|
const newConfig = Object.assign( |
|
|
|
|
{}, |
|
|
|
|
{ |
|
|
|
|
name: field, |
|
|
|
|
style: { ...style }, |
|
|
|
|
colon, |
|
|
|
|
required, |
|
|
|
|
rules, |
|
|
|
|
labelCol, |
|
|
|
|
wrapperCol, |
|
|
|
|
}, |
|
|
|
|
itemProps, |
|
|
|
|
) |
|
|
|
|
if (!itemProps?.labelCol?.span) |
|
|
|
|
newConfig.labelCol = labelCol |
|
|
|
|
|
|
|
|
|
if (!itemProps?.wrapperCol?.span) |
|
|
|
|
newConfig.wrapperCol = wrapperCol |
|
|
|
|
|
|
|
|
|
if (!itemProps?.rules) |
|
|
|
|
newConfig.rules = rules |
|
|
|
|
|
|
|
|
|
return newConfig |
|
|
|
|
}) as Recordable<any> |
|
|
|
|
|
|
|
|
|
const componentItem = computed(() => componentMap.get(props.schema.component as string)) |
|
|
|
|
|
|
|
|
|
// console.log('component change:', props.schema.component, componentItem.value); |
|
|
|
|
function handleClick(schema: IVFormComponent) { |
|
|
|
|
if (schema.component === 'Button' && schema.componentProps?.handle) |
|
|
|
|
emit(schema.componentProps?.handle) |
|
|
|
|
} |
|
|
|
|
/** |
|
|
|
|
* 处理异步属性,异步属性会导致一些属性渲染错误,如defaultValue异步加载会导致渲染不出来,故而此处只处理options,treeData,同步属性在cmpProps中处理 |
|
|
|
|
*/ |
|
|
|
|
const asyncProps = asyncComputed(async () => { |
|
|
|
|
let { options, treeData } = props.schema.componentProps ?? {} |
|
|
|
|
if (options) |
|
|
|
|
options = await handleAsyncOptions(options) |
|
|
|
|
if (treeData) |
|
|
|
|
treeData = await handleAsyncOptions(treeData) |
|
|
|
|
return { |
|
|
|
|
options, |
|
|
|
|
treeData, |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
const asyncProps = asyncComputed(async () => { |
|
|
|
|
let { options, treeData } = props.schema.componentProps ?? {} |
|
|
|
|
if (options) |
|
|
|
|
options = await handleAsyncOptions(options) |
|
|
|
|
if (treeData) |
|
|
|
|
treeData = await handleAsyncOptions(treeData) |
|
|
|
|
return { |
|
|
|
|
options, |
|
|
|
|
treeData, |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 处理同步属性 |
|
|
|
|
*/ |
|
|
|
|
const cmpProps = computed(() => { |
|
|
|
|
const isCheck = props.schema && ['Switch', 'Checkbox', 'Radio'].includes(props.schema.component) |
|
|
|
|
const { field } = props.schema |
|
|
|
|
|
|
|
|
|
let { disabled, ...attrs } = omit(props.schema.componentProps, ['options', 'treeData']) ?? {} |
|
|
|
|
|
|
|
|
|
disabled = props.formConfig.disabled || disabled |
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
...attrs, |
|
|
|
|
disabled, |
|
|
|
|
[isCheck ? 'checked' : 'value']: formData1.value[field!], |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
const handleChange = function (e) { |
|
|
|
|
const isCheck = ['Switch', 'Checkbox', 'Radio'].includes(props.schema.component) |
|
|
|
|
const target = e ? e.target : null |
|
|
|
|
const value = target ? (isCheck ? target.checked : target.value) : e |
|
|
|
|
setFormModel(props.schema.field!, value) |
|
|
|
|
emit('change', value) |
|
|
|
|
} |
|
|
|
|
return { |
|
|
|
|
...toRefs(state), |
|
|
|
|
componentItem, |
|
|
|
|
formItemProps, |
|
|
|
|
handleClick, |
|
|
|
|
asyncProps, |
|
|
|
|
cmpProps, |
|
|
|
|
handleChange, |
|
|
|
|
colPropsComputed, |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
const cmpProps = computed(() => { |
|
|
|
|
const isCheck |
|
|
|
|
= props.schema && ['Switch', 'Checkbox', 'Radio'].includes(props.schema.component) |
|
|
|
|
const { field } = props.schema |
|
|
|
|
|
|
|
|
|
let { disabled, ...attrs } |
|
|
|
|
= omit(props.schema.componentProps, ['options', 'treeData']) ?? {} |
|
|
|
|
|
|
|
|
|
disabled = props.formConfig.disabled || disabled |
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
...attrs, |
|
|
|
|
disabled, |
|
|
|
|
[isCheck ? 'checked' : 'value']: formData1.value[field!], |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
const handleChange = function (e) { |
|
|
|
|
const isCheck = ['Switch', 'Checkbox', 'Radio'].includes(props.schema.component) |
|
|
|
|
const target = e ? e.target : null |
|
|
|
|
const value = target ? (isCheck ? target.checked : target.value) : e |
|
|
|
|
setFormModel(props.schema.field!, value) |
|
|
|
|
emit('change', value) |
|
|
|
|
} |
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
<template> |
|
|
|
@ -176,8 +153,14 @@ export default defineComponent({
|
|
|
|
|
</Tooltip> |
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
<slot v-if="schema.componentProps && schema.componentProps?.slotName" :name="schema.componentProps.slotName" v-bind="schema" /> |
|
|
|
|
<Divider v-else-if="schema.component === 'Divider' && schema.label && !formItemProps.hiddenLabel"> |
|
|
|
|
<slot |
|
|
|
|
v-if="schema.componentProps && schema.componentProps?.slotName" |
|
|
|
|
:name="schema.componentProps.slotName" |
|
|
|
|
v-bind="schema" |
|
|
|
|
/> |
|
|
|
|
<Divider |
|
|
|
|
v-else-if="schema.component === 'Divider' && schema.label && !formItemProps.hiddenLabel" |
|
|
|
|
> |
|
|
|
|
{{ schema.label }} |
|
|
|
|
</Divider> |
|
|
|
|
<!-- 部分控件需要一个空div --> |
|
|
|
@ -199,20 +182,20 @@ export default defineComponent({
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
<style lang="less" scoped> |
|
|
|
|
.ml-5 { |
|
|
|
|
margin-left: 5px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// form字段中的标签有ant-col,不能使用width:100% |
|
|
|
|
:deep(.ant-col) { |
|
|
|
|
width: auto; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.ant-form-item:not(.ant-form-item-with-help) { |
|
|
|
|
margin-bottom: 20px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// .w-full { |
|
|
|
|
// width: 100% !important; |
|
|
|
|
// } |
|
|
|
|
.ml-5 { |
|
|
|
|
margin-left: 5px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// form字段中的标签有ant-col,不能使用width:100% |
|
|
|
|
:deep(.ant-col) { |
|
|
|
|
width: auto; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.ant-form-item:not(.ant-form-item-with-help) { |
|
|
|
|
margin-bottom: 20px; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// .w-full { |
|
|
|
|
// width: 100% !important; |
|
|
|
|
// } |
|
|
|
|
</style> |
|
|
|
|