| | |
| | | import { type Recordable, type Nullable } from '@vben/types'; |
| | | import type { PropType, Ref } from 'vue'; |
| | | import { computed, defineComponent, toRefs, unref } from 'vue'; |
| | | import type { FormActionType, FormProps, FormSchema } from '../types/form'; |
| | | import type { ValidationRule } from 'ant-design-vue/lib/form/Form'; |
| | | import type { TableActionType } from '/@/components/Table'; |
| | | import { |
| | | isComponentFormSchema, |
| | | type FormActionType, |
| | | type FormProps, |
| | | type FormSchemaInner as FormSchema, |
| | | } from '../types/form'; |
| | | |
| | | import type { Rule as ValidationRule } from 'ant-design-vue/lib/form/interface'; |
| | | import type { TableActionType } from '@/components/Table'; |
| | | import { Col, Divider, Form } from 'ant-design-vue'; |
| | | import { componentMap } from '../componentMap'; |
| | | import { BasicHelp } from '/@/components/Basic'; |
| | | import { isBoolean, isFunction, isNull } from '/@/utils/is'; |
| | | import { getSlot } from '/@/utils/helper/tsxHelper'; |
| | | import { BasicHelp, BasicTitle } from '@/components/Basic'; |
| | | import { isBoolean, isFunction, isNull } from '@/utils/is'; |
| | | import { getSlot } from '@/utils/helper/tsxHelper'; |
| | | import { |
| | | createPlaceholderMessage, |
| | | isIncludeSimpleComponents, |
| | | NO_AUTO_LINK_COMPONENTS, |
| | | setComponentRuleType, |
| | | } from '../helper'; |
| | | import { cloneDeep, upperFirst } from 'lodash-es'; |
| | | import { useItemLabelWidth } from '../hooks/useLabelWidth'; |
| | | import { useI18n } from '/@/hooks/web/useI18n'; |
| | | import { useI18n } from '@/hooks/web/useI18n'; |
| | | |
| | | export default defineComponent({ |
| | | name: 'BasicFormItem', |
| | |
| | | formProps: Ref<FormProps>; |
| | | }; |
| | | |
| | | // 组件 CropperAvatar 的 size 属性类型为 number |
| | | // 此处补充一个兼容 |
| | | if (schema.value.component === 'CropperAvatar' && typeof formProps.value.size === 'string') { |
| | | formProps.value.size = undefined; |
| | | } |
| | | |
| | | const itemLabelWidthProp = useItemLabelWidth(schema, formProps); |
| | | |
| | | const getValues = computed(() => { |
| | |
| | | if (isFunction(componentProps)) { |
| | | componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {}; |
| | | } |
| | | if (schema.component === 'Divider') { |
| | | if (isIncludeSimpleComponents(schema.component)) { |
| | | componentProps = Object.assign( |
| | | { type: 'horizontal' }, |
| | | { |
| | |
| | | disabled = dynamicDisabled(unref(getValues)); |
| | | } |
| | | return disabled; |
| | | }); |
| | | |
| | | const getReadonly = computed(() => { |
| | | const { readonly: globReadonly } = props.formProps; |
| | | const { dynamicReadonly } = props.schema; |
| | | const { readonly: itemReadonly = false } = unref(getComponentsProps); |
| | | |
| | | let readonly = globReadonly || itemReadonly; |
| | | if (isBoolean(dynamicReadonly)) { |
| | | readonly = dynamicReadonly; |
| | | } |
| | | if (isFunction(dynamicReadonly)) { |
| | | readonly = dynamicReadonly(unref(getValues)); |
| | | } |
| | | return readonly; |
| | | }); |
| | | |
| | | function getShow(): { isShow: boolean; isIfShow: boolean } { |
| | |
| | | isShow = isShow && itemIsAdvanced; |
| | | return { isShow, isIfShow }; |
| | | } |
| | | |
| | | function handleRules(): ValidationRule[] { |
| | | const { |
| | | rules: defRules = [], |
| | |
| | | dynamicRules, |
| | | required, |
| | | } = props.schema; |
| | | |
| | | if (isFunction(dynamicRules)) { |
| | | return dynamicRules(unref(getValues)) as ValidationRule[]; |
| | | } |
| | |
| | | const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') |
| | | ? rulesMessageJoinLabel |
| | | : globalRulesMessageJoinLabel; |
| | | const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`; |
| | | const assertLabel = joinLabel ? (isFunction(label) ? '' : label) : ''; |
| | | const defaultMsg = component |
| | | ? createPlaceholderMessage(component) + assertLabel |
| | | : assertLabel; |
| | | |
| | | function validator(rule: any, value: any) { |
| | | const msg = rule.message || defaultMsg; |
| | |
| | | } |
| | | return Promise.resolve(); |
| | | } |
| | | |
| | | const getRequired = isFunction(required) ? required(unref(getValues)) : required; |
| | | |
| | | /* |
| | |
| | | */ |
| | | if (getRequired) { |
| | | if (!rules || rules.length === 0) { |
| | | rules = [{ required: getRequired, validator }]; |
| | | const trigger = NO_AUTO_LINK_COMPONENTS.includes(component || 'Input') |
| | | ? 'blur' |
| | | : 'change'; |
| | | rules = [{ required: getRequired, validator, trigger }]; |
| | | } else { |
| | | const requiredIndex: number = rules.findIndex((rule) => Reflect.has(rule, 'required')); |
| | | |
| | |
| | | rule.required = false; |
| | | } |
| | | if (component) { |
| | | if (!Reflect.has(rule, 'type')) { |
| | | rule.type = component === 'InputNumber' ? 'number' : 'string'; |
| | | } |
| | | |
| | | rule.message = rule.message || defaultMsg; |
| | | |
| | | if (component.includes('Input') || component.includes('Textarea')) { |
| | |
| | | } |
| | | |
| | | function renderComponent() { |
| | | if (!isComponentFormSchema(props.schema)) { |
| | | return null; |
| | | } |
| | | const { |
| | | renderComponentContent, |
| | | component, |
| | | field, |
| | | changeEvent = 'change', |
| | | valueField, |
| | | valueFormat, |
| | | } = props.schema; |
| | | |
| | | const isCheck = component && ['Switch', 'Checkbox'].includes(component); |
| | |
| | | const on = { |
| | | [eventKey]: (...args: Nullable<Recordable<any>>[]) => { |
| | | const [e] = args; |
| | | |
| | | const target = e ? e.target : null; |
| | | let value = target ? (isCheck ? target.checked : target.value) : e; |
| | | if(isFunction(valueFormat)){ |
| | | value = valueFormat({...unref(getValues),value}); |
| | | } |
| | | props.setFormModel(field, value, props.schema); |
| | | |
| | | if (propsData[eventKey]) { |
| | | propsData[eventKey](...args); |
| | | } |
| | | const target = e ? e.target : null; |
| | | const value = target ? (isCheck ? target.checked : target.value) : e; |
| | | props.setFormModel(field, value, props.schema); |
| | | }, |
| | | }; |
| | | const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>; |
| | |
| | | const { autoSetPlaceHolder, size } = props.formProps; |
| | | const propsData: Recordable<any> = { |
| | | allowClear: true, |
| | | getPopupContainer: (trigger: Element) => trigger.parentNode, |
| | | size, |
| | | ...unref(getComponentsProps), |
| | | disabled: unref(getDisable), |
| | | readonly: unref(getReadonly), |
| | | }; |
| | | |
| | | const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder; |
| | |
| | | return <Comp {...compAttr} />; |
| | | } |
| | | const compSlot = isFunction(renderComponentContent) |
| | | ? { ...renderComponentContent(unref(getValues)) } |
| | | ? { |
| | | ...renderComponentContent(unref(getValues), { |
| | | disabled: unref(getDisable), |
| | | readonly: unref(getReadonly), |
| | | }), |
| | | } |
| | | : { |
| | | default: () => renderComponentContent, |
| | | }; |
| | |
| | | |
| | | function renderLabelHelpMessage() { |
| | | const { label, helpMessage, helpComponentProps, subLabel } = props.schema; |
| | | const getLabel = isFunction(label) ? label(unref(getValues)) : label; |
| | | const renderLabel = subLabel ? ( |
| | | <span> |
| | | {label} <span class="text-secondary">{subLabel}</span> |
| | | {getLabel} <span class="text-secondary">{subLabel}</span> |
| | | </span> |
| | | ) : ( |
| | | label |
| | | getLabel |
| | | ); |
| | | const getHelpMessage = isFunction(helpMessage) |
| | | ? helpMessage(unref(getValues)) |
| | |
| | | } |
| | | |
| | | function renderItem() { |
| | | const { itemProps, slot, render, field, suffix, component } = props.schema; |
| | | const { itemProps, slot, render, field, suffix, component, prefix } = props.schema; |
| | | const { labelCol, wrapperCol } = unref(itemLabelWidthProp); |
| | | const { colon } = props.formProps; |
| | | |
| | | const opts = { disabled: unref(getDisable), readonly: unref(getReadonly) }; |
| | | if (component === 'Divider') { |
| | | return ( |
| | | <Col span={24}> |
| | | <Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider> |
| | | </Col> |
| | | ); |
| | | } else if (component === 'BasicTitle') { |
| | | return ( |
| | | <Form.Item |
| | | labelCol={labelCol} |
| | | wrapperCol={wrapperCol} |
| | | name={field} |
| | | class={{ |
| | | 'suffix-item': !!suffix, |
| | | 'prefix-item': !!prefix, |
| | | }} |
| | | > |
| | | <BasicTitle {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</BasicTitle> |
| | | </Form.Item> |
| | | ); |
| | | } else { |
| | | const getContent = () => { |
| | | return slot |
| | | ? getSlot(slots, slot, unref(getValues)) |
| | | ? getSlot(slots, slot, unref(getValues), opts) |
| | | : render |
| | | ? render(unref(getValues)) |
| | | : renderComponent(); |
| | | ? render(unref(getValues), opts) |
| | | : renderComponent(); |
| | | }; |
| | | |
| | | const showSuffix = !!suffix; |
| | | const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix; |
| | | |
| | | const showPrefix = !!prefix; |
| | | const getPrefix = isFunction(prefix) ? prefix(unref(getValues)) : prefix; |
| | | // TODO 自定义组件验证会出现问题,因此这里框架默认将自定义组件设置手动触发验证,如果其他组件还有此问题请手动设置autoLink=false |
| | | if (NO_AUTO_LINK_COMPONENTS.includes(component)) { |
| | | if (component && NO_AUTO_LINK_COMPONENTS.includes(component)) { |
| | | props.schema && |
| | | (props.schema.itemProps! = { |
| | | autoLink: false, |
| | |
| | | <Form.Item |
| | | name={field} |
| | | colon={colon} |
| | | class={{ 'suffix-item': showSuffix }} |
| | | class={{ |
| | | 'suffix-item': showSuffix, |
| | | 'prefix-item': showPrefix, |
| | | }} |
| | | {...(itemProps as Recordable<any>)} |
| | | label={renderLabelHelpMessage()} |
| | | rules={handleRules()} |
| | |
| | | wrapperCol={wrapperCol} |
| | | > |
| | | <div style="display:flex"> |
| | | {showPrefix && <span class="prefix">{getPrefix}</span>} |
| | | <div style="flex:1;">{getContent()}</div> |
| | | {showSuffix && <span class="suffix">{getSuffix}</span>} |
| | | </div> |
| | |
| | | } |
| | | |
| | | return () => { |
| | | const { colProps = {}, colSlot, renderColContent, component } = props.schema; |
| | | if (!componentMap.has(component)) { |
| | | const { colProps = {}, colSlot, renderColContent, component, slot } = props.schema; |
| | | if (!((component && componentMap.has(component)) || slot)) { |
| | | return null; |
| | | } |
| | | |
| | |
| | | const realColProps = { ...baseColProps, ...colProps }; |
| | | const { isIfShow, isShow } = getShow(); |
| | | const values = unref(getValues); |
| | | const opts = { disabled: unref(getDisable), readonly: unref(getReadonly) }; |
| | | |
| | | const getContent = () => { |
| | | return colSlot |
| | | ? getSlot(slots, colSlot, values) |
| | | ? getSlot(slots, colSlot, values, opts) |
| | | : renderColContent |
| | | ? renderColContent(values) |
| | | : renderItem(); |
| | | ? renderColContent(values, opts) |
| | | : renderItem(); |
| | | }; |
| | | |
| | | return ( |