formSchema props支持动态修改

src/components/Form/src/components/FormItem.vue

<script lang="tsx">
  import { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
  import type { PropType, Ref } from 'vue';
  import type { FormActionType, FormProps } from '../types/form';
  import type { FormSchema } from '../types/form';
  import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
  import type { TableActionType } from '/@/components/Table';
  import { defineComponent, computed, unref, toRefs } from 'vue';
  import { Form, Col, Divider } 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 { createPlaceholderMessage, setComponentRuleType } from '../helper';
  import { upperFirst, cloneDeep } from 'lodash-es';
  import { useItemLabelWidth } from '../hooks/useLabelWidth';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { useAppInject } from '/@/hooks/web/useAppInject';

  export default defineComponent({
    name: 'BasicFormItem',
    inheritAttrs: false,
    props: {
      schema: {
        type: Object as PropType<FormSchema>,
        default: () => ({}),
      },
      formProps: {
        type: Object as PropType<FormProps>,
        default: () => ({}),
      },
      allDefaultValues: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      formModel: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      setFormModel: {
        type: Function as PropType<(key: string, value: any) => void>,
        default: null,
      },
      validateFields: {
        type: Function as PropType<(nameList?: NamePath[] | undefined, options?: ValidateOptions) => Promise<any>>,
        default: null,
      },
      tableAction: {
        type: Object as PropType<TableActionType>,
      },
      formActionType: {
        type: Object as PropType<FormActionType>,
      },
    },
    setup(props, { slots }) {
      const { t } = useI18n();

      const { schema, formProps } = toRefs(props) as {
        schema: Ref<FormSchema>;
        formProps: Ref<FormProps>;
      };

      const itemLabelWidthProp = useItemLabelWidth(schema, formProps);

      const getValues = computed(() => {
        const { allDefaultValues, formModel, schema } = props;
        const { mergeDynamicData } = props.formProps;
        return {
          field: schema.field,
          model: formModel,
          values: {
            ...mergeDynamicData,
            ...allDefaultValues,
            ...formModel,
          } as Recordable,
          schema: schema,
        };
      });

      const getComponentsProps = computed(() => {
        const { schema, tableAction, formModel, formActionType } = props;
        let { componentProps = {} } = schema;
        if (isFunction(componentProps)) {
          componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
        }
        if (schema.component === 'Divider') {
          //update-begin---author:wangshuai---date:2023-09-22---for:【QQYUN-6603】分割线标题位置显示不正确---
          componentProps = Object.assign({ type: 'horizontal',orientation:'left', plain: true, }, componentProps);
          //update-end---author:wangshuai---date:2023-09-22---for:【QQYUN-6603】分割线标题位置显示不正确---
        }
        return componentProps as Recordable;
      });

      const getDisable = computed(() => {
        const { disabled: globDisabled } = props.formProps;
        const { dynamicDisabled } = props.schema;
        const { disabled: itemDisabled = false } = unref(getComponentsProps);
        let disabled = !!globDisabled || itemDisabled;
        if (isBoolean(dynamicDisabled)) {
          disabled = dynamicDisabled;
        }
        if (isFunction(dynamicDisabled)) {
          disabled = dynamicDisabled(unref(getValues));
        }
        return disabled;
      });

      // update-begin--author:liaozhiyang---date:20240308---for:【QQYUN-8377】formSchema props支持动态修改
      const getDynamicPropsValue = computed(() => {
        const { dynamicPropsVal, dynamicPropskey } = props.schema;
        if (dynamicPropskey == null) {
          return null;
        } else {
          const { [dynamicPropskey]: itemValue } = unref(getComponentsProps);
          let value = itemValue;
          if (isFunction(dynamicPropsVal)) {
            value = dynamicPropsVal(unref(getValues));
            return value;
          }
        }
      });
      // update-end--author:liaozhiyang---date:20240308---for:【QQYUN-8377】formSchema props支持动态修改

      function getShow(): { isShow: boolean; isIfShow: boolean } {
        const { show, ifShow } = props.schema;
        const { showAdvancedButton } = props.formProps;
        const itemIsAdvanced = showAdvancedButton ? (isBoolean(props.schema.isAdvanced) ? props.schema.isAdvanced : true) : true;

        let isShow = true;
        let isIfShow = true;

        if (isBoolean(show)) {
          isShow = show;
        }
        if (isBoolean(ifShow)) {
          isIfShow = ifShow;
        }
        if (isFunction(show)) {
          isShow = show(unref(getValues));
        }
        if (isFunction(ifShow)) {
          isIfShow = ifShow(unref(getValues));
        }
        isShow = isShow && itemIsAdvanced;
        return { isShow, isIfShow };
      }

      function handleRules(): ValidationRule[] {
        const { rules: defRules = [], component, rulesMessageJoinLabel, label, dynamicRules, required } = props.schema;

        if (isFunction(dynamicRules)) {
          return dynamicRules(unref(getValues)) as ValidationRule[];
        }

        let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];
        const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;

        const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') ? rulesMessageJoinLabel : globalRulesMessageJoinLabel;
        const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;

        function validator(rule: any, value: any) {
          const msg = rule.message || defaultMsg;
          if (value === undefined || isNull(value)) {
            // 空值
            return Promise.reject(msg);
          } else if (Array.isArray(value) && value.length === 0) {
            // 数组类型
            return Promise.reject(msg);
          } else if (typeof value === 'string' && value.trim() === '') {
            // 空字符串
            return Promise.reject(msg);
          } else if (
            typeof value === 'object' &&
            Reflect.has(value, 'checked') &&
            Reflect.has(value, 'halfChecked') &&
            Array.isArray(value.checked) &&
            Array.isArray(value.halfChecked) &&
            value.checked.length === 0 &&
            value.halfChecked.length === 0
          ) {
            // 非关联选择的tree组件
            return Promise.reject(msg);
          }
          return Promise.resolve();
        }

        const getRequired = isFunction(required) ? required(unref(getValues)) : required;

        if ((!rules || rules.length === 0) && getRequired) {
          rules = [{ required: getRequired, validator }];
        }

        const requiredRuleIndex: number = rules.findIndex((rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator'));

        if (requiredRuleIndex !== -1) {
          const rule = rules[requiredRuleIndex];
          const { isShow } = getShow();
          if (!isShow) {
            rule.required = false;
          }
          if (component) {
            //update-begin---author:wangshuai---date:2024-02-01---for:【QQYUN-8176】编辑表单中,校验必填时,如果组件是ApiSelect,打开编辑页面时,即使该字段有值,也会提示请选择---
            //https://github.com/vbenjs/vue-vben-admin/pull/3082 github修复原文
            /*if (!Reflect.has(rule, 'type')) {
              rule.type = component === 'InputNumber' ? 'number' : 'string';
            }*/
            //update-end---author:wangshuai---date:2024-02-01---for:【QQYUN-8176】编辑表单中,校验必填时,如果组件是ApiSelect,打开编辑页面时,即使该字段有值,也会提示请选择---

            rule.message = rule.message || defaultMsg;

            if (component.includes('Input') || component.includes('Textarea')) {
              rule.whitespace = true;
            }
            const valueFormat = unref(getComponentsProps)?.valueFormat;
            setComponentRuleType(rule, component, valueFormat);
          }
        }

        // Maximum input length rule check
        const characterInx = rules.findIndex((val) => val.max);
        if (characterInx !== -1 && !rules[characterInx].validator) {
          rules[characterInx].message = rules[characterInx].message || t('component.form.maxTip', [rules[characterInx].max] as Recordable);
        }
        // update-begin--author:liaozhiyang---date:20241226---for:【QQYUN-7495】pattern由字符串改成正则传递给antd(因使用InputNumber时发现正则无效)
        rules.forEach((item) => {
          if (typeof item.pattern === 'string') {
            try {
              const reg = new Function('item', `return ${item.pattern}`)(item);
              if (Object.prototype.toString.call(reg) === '[object RegExp]') {
                item.pattern = reg;
              } else {
                item.pattern = new RegExp(item.pattern);
              }
            } catch (error) {
              item.pattern = new RegExp(item.pattern);
            }
          }
        });
        // update-end--author:liaozhiyang---date:20231226---for:【QQYUN-7495】pattern由字符串改成正则传递给antd(因使用InputNumber时发现正则无效)
        return rules;
      }

      function renderComponent() {
        const { renderComponentContent, component, field, changeEvent = 'change', valueField, componentProps } = props.schema;

        const isCheck = component && ['Switch', 'Checkbox'].includes(component);
        // update-begin--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
        let isTrim = false;
        if (component === 'Input' && componentProps && componentProps.trim) {
          isTrim = true;
        }
        // update-end--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
        const eventKey = `on${upperFirst(changeEvent)}`;
        // update-begin--author:liaozhiyang---date:20230922---for:【issues/752】表单校验dynamicRules 无法 使用失去焦点后校验 trigger: 'blur'
        const on = {
          [eventKey]: (...args: Nullable<Recordable>[]) => {
            const [e] = args;
            if (propsData[eventKey]) {
              propsData[eventKey](...args);
            }
            const target = e ? e.target : null;
            // update-begin--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
            let value;
            if (target) {
              if (isCheck) {
                value = target.checked;
              } else {
                value = isTrim ? target.value.trim() : target.value;
              }
            } else {
              value = e;
            }
            // update-end--author:liaozhiyang---date:20231013---for:【QQYUN-6679】input去空格
            props.setFormModel(field, value);
            //props.validateFields([field], { triggerName: 'change' }).catch((_) => {});
          },
          // onBlur: () => {
          //   props.validateFields([field], { triggerName: 'blur' }).catch((_) => {});
          // },
        };
        // update-end--author:liaozhiyang---date:20230922---for:【issues/752】表单校验dynamicRules 无法 使用失去焦点后校验 trigger: 'blur'
        const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;

        const { autoSetPlaceHolder, size } = props.formProps;
        const propsData: Recordable = {
          allowClear: true,
          getPopupContainer: (trigger: Element) => {

            return trigger?.parentNode;
          },
          size,
          ...unref(getComponentsProps),
          disabled: unref(getDisable),
        };
        // update-begin--author:liaozhiyang---date:20240308---for:【QQYUN-8377】formSchema props支持动态修改
        const dynamicPropskey = props.schema.dynamicPropskey;
        if (dynamicPropskey) {
          propsData[dynamicPropskey] = unref(getDynamicPropsValue);
        }
        // update-end--author:liaozhiyang---date:20240308---for:【QQYUN-8377】formSchema props支持动态修改

        const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;
        // RangePicker place是一个数组
        if (isCreatePlaceholder && component !== 'RangePicker' && component) {
          //自动设置placeholder
          propsData.placeholder = unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component) + props.schema.label;
        }
        propsData.codeField = field;
        propsData.formValues = unref(getValues);

        const bindValue: Recordable = {
          [valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
        };

        const compAttr: Recordable = {
          ...propsData,
          ...on,
          ...bindValue,
        };

        if (!renderComponentContent) {
          return <Comp {...compAttr} />;
        }
        const compSlot = isFunction(renderComponentContent)
          ? { ...renderComponentContent(unref(getValues)) }
          : {
              default: () => renderComponentContent,
            };
        return <Comp {...compAttr}>{compSlot}</Comp>;
      }

      /**
       *渲染Label
       * @updateBy:zyf
       */
      function renderLabelHelpMessage() {
        //update-begin-author:taoyan date:2022-9-7 for: VUEN-2061【样式】online表单超出4个 .. 省略显示
        //label宽度支持自定义
        const { label, helpMessage, helpComponentProps, subLabel, labelLength } = props.schema;
        let showLabel: string = label + '';
        if (labelLength && showLabel.length > 4) {
          showLabel = showLabel.substr(0, labelLength);
        }
        const titleObj = { title: label };
        const renderLabel = subLabel ? (
          <span>
            {label} <span class="text-secondary">{subLabel}</span>
          </span>
        ) : labelLength ? (
          <label {...titleObj}>{showLabel}</label>
        ) : (
          label
        );
        //update-end-author:taoyan date:2022-9-7 for: VUEN-2061【样式】online表单超出4个 .. 省略显示
        const getHelpMessage = isFunction(helpMessage) ? helpMessage(unref(getValues)) : helpMessage;
        if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
          return renderLabel;
        }
        return (
          <span>
            {renderLabel}
            <BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} />
          </span>
        );
      }

      function renderItem() {
        const { itemProps, slot, render, field, suffix, component } = props.schema;
        const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
        const { colon } = props.formProps;

        if (component === 'Divider') {
          return (
            <Col span={24}>
              <Divider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider>
            </Col>
          );
        } else {
          const getContent = () => {
            return slot ? getSlot(slots, slot, unref(getValues)) : render ? render(unref(getValues)) : renderComponent();
          };

          const showSuffix = !!suffix;
          const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;

          return (
            <Form.Item
              name={field}
              colon={colon}
              class={{ 'suffix-item': showSuffix }}
              {...(itemProps as Recordable)}
              label={renderLabelHelpMessage()}
              rules={handleRules()}
              labelCol={labelCol}
              wrapperCol={wrapperCol}
            >
              <div style="display:flex">
                {/* author: sunjianlei for: 【VUEN-744】此处加上 width: 100%; 因为要防止组件宽度超出 FormItem */}
                <div style="flex:1; width: 100%;">{getContent()}</div>
                {showSuffix && <span class="suffix">{getSuffix}</span>}
              </div>
            </Form.Item>
          );
        }
      }

      return () => {
        const { colProps = {}, colSlot, renderColContent, component } = props.schema;
        if (!componentMap.has(component)) {
          return null;
        }

        const { baseColProps = {} } = props.formProps;
        // update-begin--author:liaozhiyang---date:20230803---for:【issues-641】调整表格搜索表单的span配置无效
        const { getIsMobile } = useAppInject();
        let realColProps;
        realColProps = { ...baseColProps, ...colProps };
        if (colProps['span'] && !unref(getIsMobile)) {
          ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].forEach((name) => delete realColProps[name]);
        }
        // update-end--author:liaozhiyang---date:20230803---for:【issues-641】调整表格搜索表单的span配置无效
        const { isIfShow, isShow } = getShow();
        const values = unref(getValues);

        const getContent = () => {
          return colSlot ? getSlot(slots, colSlot, values) : renderColContent ? renderColContent(values) : renderItem();
        };

        return (
          isIfShow && (
            <Col {...realColProps} v-show={isShow}>
              {getContent()}
            </Col>
          )
        );
      };
    },
  });
</script>

src/components/Form/src/types/form.ts

import type { NamePath, RuleObject, ValidateOptions } from 'ant-design-vue/lib/form/interface';
import type { VNode, ComputedRef } from 'vue';
import type { ButtonProps as AntdButtonProps } from '/@/components/Button';
import type { FormItem } from './formItem';
import type { ColEx, ComponentType } from './index';
import type { TableActionType } from '/@/components/Table/src/types/table';
import type { CSSProperties } from 'vue';
import type { RowProps } from 'ant-design-vue/lib/grid/Row';

export type FieldMapToTime = [string, [string, string], string?][];
export type FieldMapToNumber = [string, [string, string]][];

export type Rule = RuleObject & {
  trigger?: 'blur' | 'change' | ['change', 'blur'];
};

export interface RenderCallbackParams {
  schema: FormSchema;
  values: Recordable;
  model: Recordable;
  field: string;
}

export interface ButtonProps extends AntdButtonProps {
  text?: string;
}

export interface FormActionType {
  submit: () => Promise<void>;
  setFieldsValue: <T>(values: T) => Promise<void>;
  resetFields: () => Promise<void>;
  getFieldsValue: () => Recordable;
  clearValidate: (name?: string | string[]) => Promise<void>;
  updateSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
  resetSchema: (data: Partial<FormSchema> | Partial<FormSchema>[]) => Promise<void>;
  setProps: (formProps: Partial<FormProps>) => Promise<void>;
  getProps: ComputedRef<Partial<FormProps>>;
  removeSchemaByFiled: (field: string | string[]) => Promise<void>;
  appendSchemaByField: (schema: FormSchema, prefixField: string | undefined, first?: boolean | undefined) => Promise<void>;
  validateFields: (nameList?: NamePath[], options?: ValidateOptions) => Promise<any>;
  validate: (nameList?: NamePath[]) => Promise<any>;
  scrollToField: (name: NamePath, options?: ScrollOptions) => Promise<void>;
}

export type RegisterFn = (formInstance: FormActionType) => void;

export type UseFormReturnType = [RegisterFn, FormActionType];

export interface FormProps {
  layout?: 'vertical' | 'inline' | 'horizontal';
  // Form value
  model?: Recordable;
  // The width of all items in the entire form
  labelWidth?: number | string;
  //alignment
  labelAlign?: 'left' | 'right';
  //Row configuration for the entire form
  rowProps?: RowProps;
  // Submit form on reset
  submitOnReset?: boolean;
  // Col configuration for the entire form
  labelCol?: Partial<ColEx> | null;
  // Col configuration for the entire form
  wrapperCol?: Partial<ColEx> | null;

  // General row style
  baseRowStyle?: CSSProperties;

  // General col configuration
  baseColProps?: Partial<ColEx>;

  // Form configuration rules
  schemas?: FormSchema[];
  // Function values used to merge into dynamic control form items
  mergeDynamicData?: Recordable;
  // Compact mode for search forms
  compact?: boolean;
  // Blank line span
  emptySpan?: number | Partial<ColEx>;
  // Internal component size of the form
  size?: 'default' | 'small' | 'large';
  // Whether to disable
  disabled?: boolean;
  // Time interval fields are mapped into multiple
  fieldMapToTime?: FieldMapToTime;
  // number interval fields are mapped into multiple
  fieldMapToNumber?: FieldMapToNumber;
  // Placeholder is set automatically
  autoSetPlaceHolder?: boolean;
  // Auto submit on press enter on input
  autoSubmitOnEnter?: boolean;
  // Check whether the information is added to the label
  rulesMessageJoinLabel?: boolean;
  // 是否显示展开收起按钮
  showAdvancedButton?: boolean;
  // Whether to focus on the first input box, only works when the first form item is input
  autoFocusFirstItem?: boolean;
  // 【jeecg】如果 showAdvancedButton 为 true,超过指定列数默认折叠,默认为3
  autoAdvancedCol?: number;
  // 如果 showAdvancedButton 为 true,超过指定行数行默认折叠
  autoAdvancedLine?: number;
  // 折叠时始终保持显示的行数
  alwaysShowLines?: number;
  // Whether to show the operation button
  showActionButtonGroup?: boolean;

  // Reset button configuration
  resetButtonOptions?: Partial<ButtonProps>;

  // Confirm button configuration
  submitButtonOptions?: Partial<ButtonProps>;

  // Operation column configuration
  actionColOptions?: Partial<ColEx>;

  // Show reset button
  showResetButton?: boolean;
  // Show confirmation button
  showSubmitButton?: boolean;

  resetFunc?: () => Promise<void>;
  submitFunc?: () => Promise<void>;
  transformDateFunc?: (date: any) => string;
  colon?: boolean;
}
export interface FormSchema {
  // Field name
  field: string;
  // Event name triggered by internal value change, default change
  changeEvent?: string;
  // Variable name bound to v-model Default value
  valueField?: string;
  // Label name
  label: string | VNode;
  // Auxiliary text
  subLabel?: string;
  // Help text on the right side of the text
  helpMessage?: string | string[] | ((renderCallbackParams: RenderCallbackParams) => string | string[]);
  // BaseHelp component props
  helpComponentProps?: Partial<HelpComponentProps>;
  // Label width, if it is passed, the labelCol and WrapperCol configured by itemProps will be invalid
  labelWidth?: string | number;
  // Disable the adjustment of labelWidth with global settings of formModel, and manually set labelCol and wrapperCol by yourself
  disabledLabelWidth?: boolean;
  // render component
  component: ComponentType;
  // Component parameters
  componentProps?:
    | ((opt: { schema: FormSchema; tableAction: TableActionType; formActionType: FormActionType; formModel: Recordable }) => Recordable)
    | object;
  // Required
  required?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);

  suffix?: string | number | ((values: RenderCallbackParams) => string | number);

  // Validation rules
  rules?: Rule[];
  // Check whether the information is added to the label
  rulesMessageJoinLabel?: boolean;

  // Reference formModelItem
  itemProps?: Partial<FormItem>;

  // col configuration outside formModelItem
  colProps?: Partial<ColEx>;

  // 默认值
  defaultValue?: any;
  isAdvanced?: boolean;

  // Matching details components
  span?: number;

  ifShow?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);

  show?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);

  // Render the content in the form-item tag
  render?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;

  // Rendering col content requires outer wrapper form-item
  renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string;

  renderComponentContent?: ((renderCallbackParams: RenderCallbackParams) => any) | VNode | VNode[] | string;

  // Custom slot, in from-item
  slot?: string;

  // Custom slot, similar to renderColContent
  colSlot?: string;

  dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean);

  dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[];
  // update-begin--author:liaozhiyang---date:20240308---for:【QQYUN-8377】formSchema props支持动态修改
  // 设置组件props的key
  dynamicPropskey?: string;
  dynamicPropsVal?: ((renderCallbackParams: RenderCallbackParams) => any);
  // update-end--author:liaozhiyang---date:20240308---for:【QQYUN-8377】formSchema props支持动态修改

  // 这个属性自定义的 用于自定义的业务 比如在表单打开的时候修改表单的禁用状态,但是又不能重写componentProps,因为他的内容太多了,所以使用dynamicDisabled和buss实现
  buss?: any;
  
  //label字数控制(label宽度)
  labelLength?: number
}
export interface HelpComponentProps {
  maxWidth: string;
  // Whether to display the serial number
  showIndex: boolean;
  // Text list
  text: any;
  // colour
  color: string;
  // font size
  fontSize: string;
  icon: string;
  absolute: boolean;
  // Positioning
  position: any;
}

src/views/demo/jeecg/JeecgComponents.vue

<template>
  <BasicForm
    ref="formElRef"
    :class="'jee-select-demo-form'"
    :labelCol="{ span: 6 }"
    :wrapperCol="{ span: 14 }"
    :showResetButton="false"
    :showSubmitButton="false"
    :schemas="schemas"
    :actionColOptions="{ span: 24 }"
    @submit="handleSubmit"
    @reset="handleReset"
    style="height: 100%"
  >
    <template #jAreaLinkage="{ model, field }">
      <JAreaLinkage v-model:value="model[field]" :showArea="true" :showAll="false" />
    </template>
    <template #jAreaLinkage1="{ model, field }">
      <JAreaLinkage :disabled="isDisabledAuth(['demo.dbarray'])" v-model:value="model[field]" :showArea="true" :showAll="false" />
    </template>
    <template #JPopup="{ model, field }">
      <JPopup v-model:value="model[field]" :formElRef="formElRef" code="report_user" :fieldConfig="[{ source: 'username', target: 'pop1' }]" />
    </template>
    <template #JAreaSelect="{ model, field }">
      <JAreaSelect v-model:value="model[field]" />
    </template>
    <template #JCheckbox="{ model, field }">
      <JCheckbox v-model:value="model[field]" dictCode="remindMode" />
    </template>
    <template #JInput="{ model, field }">
      <JInput v-model:value="model[field]" :type="model['jinputtype']" />
    </template>
    <template #dargVerify="{ model, field }">
      <BasicDragVerify v-model:value="model[field]" />
    </template>
    <template #superQuery="{ model, field }">
      <super-query :config="superQueryConfig" @search="(value)=>handleSuperQuery(value, model, field)"/>
    </template>
  </BasicForm>
</template>
<script lang="ts">
  import { computed, defineComponent, unref, ref } from 'vue';
  import { BasicForm, ApiSelect, JAreaLinkage, JPopup, JAreaSelect, FormActionType, JCheckbox, JInput, JEllipsis } from '/@/components/Form';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { optionsListApi } from '/@/api/demo/select';
  import { useDebounceFn } from '@vueuse/core';
  import { schemas } from './jeecgComponents.data';
  import { usePermission } from '/@/hooks/web/usePermission';
  import { BasicDragVerify } from '/@/components/Verify';

  export default defineComponent({
    components: {
      BasicForm,
      ApiSelect,
      JAreaLinkage,
      JPopup,
      JAreaSelect,
      JCheckbox,
      JInput,
      JEllipsis,
      BasicDragVerify,
    },
    name: 'JeecgComponents',
    setup() {
      const { isDisabledAuth } = usePermission();
      const check = ref(null);
      const formElRef = ref<Nullable<FormActionType>>(null);
      const { createMessage } = useMessage();
      const keyword = ref<string>('');
      const submitButtonOptions = ref({
        text: '确定',
      });
      const searchParams = computed<Recordable>(() => {
        return { keyword: unref(keyword) };
      });

      function onSearch(value: string) {
        keyword.value = value;
      }
      
      const superQueryConfig = {
        name:{ title: "名称", view: "text", type: "string", order: 1 },
        birthday:{ title: "生日", view: "date", type: "string", order: 2 },
        age:{ title: "年龄", view: "number", type: "number", order: 4 },
        sex:{ title: "性别", view: "list", type: "string", dictCode: "sex", order: 5 },
        bpmStatus:{ title: "流程状态", view: "list_multi", type: "string",  dictCode: "bpm_status", order: 6 },
      }
      function handleSuperQuery(value, model, field){
        if(value){
          let str = decodeURI(value.superQueryParams)
          console.log(str)
          model[field] = str
        }
      }

      return {
        schemas,
        formElRef,
        isDisabledAuth,
        optionsListApi,
        submitButtonOptions,
        onSearch: useDebounceFn(onSearch, 300),
        searchParams,
        superQueryConfig,
        handleSuperQuery,
        handleReset: () => {
          keyword.value = '';
        },
        handleSubmit: (values: any) => {
          console.log('values:', values);
          createMessage.success('click search,values:' + JSON.stringify(values));
        },
        check,
      };
    },
  });
</script>
<style lang="less" scoped>
  /**update-begin-author:taoyan date:20220324 for: VUEN-351【vue3】展示不全*/
  .jee-select-demo-form .ant-col-5 {
    flex: 0 0 159px;
    max-width: 159px;
  }
  /**update-end-author:taoyan date:20220324 for: VUEN-351【vue3】展示不全*/
</style>

src/views/demo/jeecg/jeecgComponents.data.ts

import { FormSchema, JCronValidator } from '/@/components/Form';
import { usePermission } from '/@/hooks/web/usePermission';

const { isDisabledAuth } = usePermission();
export const schemas: FormSchema[] = [
  {
    field: 'jdst',
    component: 'JDictSelectTag',
    label: '性别下拉',
    helpMessage: ['component模式'],
    componentProps: {
      dictCode: 'sex',
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'jdst',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'jdst1',
    component: 'JDictSelectTag',
    label: '性别选择',
    helpMessage: ['component模式'],
    componentProps: {
      dictCode: 'sex',
      type: 'radioButton',
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'jdst1',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'jdst2',
    component: 'JDictSelectTag',
    label: '字典表下拉',
    helpMessage: ['component模式'],
    componentProps: {
      dictCode: 'sys_user,realname,id',
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'jdst2',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'jdst3',
    component: 'JDictSelectTag',
    label: '字典表下拉(带条件)',
    helpMessage: ['component模式'],
    componentProps: {
      dictCode: "sys_user,realname,id,username!='admin' order by create_time",
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'jdst3',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'jsst',
    component: 'JSearchSelect',
    label: '字典搜索(同步)',
    colProps: { span: 12 },
    componentProps: {
      //dict: "sys_depart,depart_name,id",
      dictOptions: [
        {
          text: '选项一',
          value: '1',
        },
        {
          text: '选项二',
          value: '2',
        },
        {
          text: '选项三',
          value: '3',
        },
      ],
    },
  },
  {
    field: 'jsst',
    component: 'JEllipsis',
    label: '选择值',
    colProps: { span: 12 },
  },
  {
    field: 'jsst2',
    component: 'JSearchSelect',
    label: '字典搜索(异步)',
    colProps: { span: 12 },
    componentProps: {
      dict: 'sys_depart,depart_name,id',
      pageSize: 6,
      async: true,
    },
  },
  {
    field: 'jsst2',
    component: 'JEllipsis',
    label: '选择值',
    colProps: { span: 12 },
  },
  {
    field: 'xldx',
    component: 'JDictSelectTag',
    label: '字典下拉多选',
    colProps: { span: 12 },
    componentProps: {
      dictCode: 'sex',
      mode: 'multiple',
    },
  },
  {
    field: 'xldx',
    component: 'JEllipsis',
    label: '选择值',
    colProps: { span: 12 },
  },
  {
    field: 'xldx2',
    component: 'JSelectMultiple',
    label: '字典下拉多选2',
    colProps: { span: 12 },
    componentProps: {
      dictCode: 'sex',
    },
  },
  {
    field: 'xldx2',
    component: 'JEllipsis',
    label: '选择值',
    colProps: { span: 12 },
  },
  {
    field: 'dxxlk',
    component: 'JDictSelectTag',
    label: '字典下拉单选',
    colProps: { span: 12 },
    componentProps: {
      dictCode: 'sex',
    },
  },
  {
    field: 'dxxlk',
    component: 'JEllipsis',
    label: '选择值',
    colProps: { span: 12 },
  },
  {
    label: '可输入下拉',
    field: 'selectInput',
    component: 'JSelectInput',
    componentProps: {
      options: [
        { label: '选项一', value: '1' },
        { label: '选项二', value: '2' },
        { label: '选项三', value: '3' },
      ],
    },
    colProps: { span: 12 },
  },
  {
    field: 'selectInput',
    component: 'JEllipsis',
    label: '选择值',
    colProps: { span: 12 },
  },
  {
    field: 'depart3',
    component: 'JSelectDept',
    label: '选择部门—自定义值',
    helpMessage: ['component模式'],
    componentProps: { showButton: false, rowKey: 'orgCode', primaryKey: 'orgCode' },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'depart3',
    component: 'JEllipsis',
    label: '选中部门',
    colProps: { span: 12 },
  },
  {
    field: 'depart2',
    component: 'JSelectDept',
    label: '选择部门',
    helpMessage: ['component模式'],
    componentProps: { showButton: false },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'depart2',
    component: 'JEllipsis',
    label: '选中部门',
    colProps: { span: 12 },
  },
  {
    field: 'user2',
    component: 'JSelectUser',
    label: '用户选择组件',
    helpMessage: ['component模式'],
    componentProps: {
      labelKey: 'realname',
      rowKey: 'id',
      showSelected: true,
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'user2',
    component: 'JEllipsis',
    label: '选中用户',
    colProps: { span: 12 },
  },
  {
    field: 'user3',
    component: 'JSelectUserByDept',
    label: '部门选择用户',
    helpMessage: ['component模式'],
    componentProps: {
      labelKey: 'realname',
      rowKey: 'username',
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'user3',
    component: 'JEllipsis',
    label: '选中用户',
    colProps: { span: 12 },
  },
  {
    field: 'role2',
    component: 'JSelectRole',
    label: '角色选择组件',
    helpMessage: ['component模式'],
    colProps: {
      span: 12,
    },
  },
  {
    field: 'role2',
    component: 'JEllipsis',
    label: '选中角色',
    colProps: { span: 12 },
  },
  {
    field: 'position2',
    component: 'JSelectPosition',
    label: '职务选择组件',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
    componentProps: { async: true, showSelectTable: true },
  },
  {
    field: 'position2',
    component: 'JEllipsis',
    label: '选中职务',
    colProps: { span: 12 },
  },
  {
    field: 'checkbox1',
    component: 'JCheckbox',
    label: 'JCheckbox组件1',
    helpMessage: ['component模式'],
    defaultValue: '1,2',
    componentProps: {
      options: [
        { label: '男', value: '1' },
        { label: '女', value: '2' },
      ],
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'checkbox1',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'checkbox2',
    component: 'Input',
    label: 'JCheckbox组件2',
    defaultValue: '1',
    helpMessage: ['插槽模式'],
    slot: 'JCheckbox',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'checkbox2',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'data1',
    label: '日期选择',
    component: 'DatePicker',
    componentProps: {
      showTime: true,
      valueFormat: 'YYYY-MM-DD HH:mm:ss',
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'data1',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'data2',
    label: '年份范围选择',
    component: 'RangePicker',
    componentProps: {
      picker: 'year',
      valueFormat: 'YYYY',
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'data2',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'hk',
    component: 'Input',
    label: '滑块验证码',
    helpMessage: ['插槽模式'],
    slot: 'dargVerify',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'hk',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'JTreeDict',
    component: 'JTreeDict',
    label: '树字典',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
  },
  {
    field: 'JTreeDict',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'ts',
    component: 'JTreeSelect',
    label: '下拉树选择',
    helpMessage: ['component模式'],
    componentProps: {
      dict: 'sys_permission,name,id',
      pidField: 'parent_id',
      hasChildField: 'is_leaf',
      converIsLeafVal: 0,
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'ts',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'ts1',
    component: 'JTreeSelect',
    label: '下拉树多选',
    helpMessage: ['component模式'],
    componentProps: {
      dict: 'sys_permission,name,id',
      pidField: 'parent_id',
      hasChildField: 'is_leaf',
      converIsLeafVal: 0,
      multiple: true,
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'ts1',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'category',
    component: 'JCategorySelect',
    label: '分类字典树',
    helpMessage: ['component模式'],
    defaultValue: '',
    componentProps: {
      pcode: 'B01',
      multiple: true,
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'category',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'JEasyCron',
    component: 'JEasyCron',
    label: 'JEasyCron',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
    defaultValue: '* * * * * ? *',
    rules: [{ validator: JCronValidator }],
  },
  {
    field: 'JEasyCron',
    component: 'JEllipsis',
    label: '选择值',
    colProps: { span: 12 },
  },
  {
    field: 'JInput',
    component: 'JInput',
    label: '特殊查询组件',
    helpMessage: ['插槽模式'],
    slot: 'JInput',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'jinputtype',
    component: 'Select',
    label: '查询类型',
    componentProps: {
      options: [
        { value: 'like', label: '模糊(like)' },
        { value: 'ne', label: '不等于(ne)' },
        { value: 'ge', label: '大于等于(ge)' },
        { value: 'le', label: '小于等于(le)' },
      ],
    },
    colProps: {
      span: 6,
    },
  },
  {
    field: 'JInput',
    component: 'JEllipsis',
    label: '输入值',
    colProps: { span: 6 },
  },
  {
    field: 'field1',
    component: 'Select',
    label: '省市区选择',
    helpMessage: ['插槽模式'],
    slot: 'jAreaLinkage',
    colProps: {
      span: 12,
    },
    defaultValue: ['130000', '130200'],
  },
  {
    field: 'field1',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'field0',
    component: 'Select',
    label: '禁用组件(方式一)',
    helpMessage: ['插槽模式'],
    slot: 'jAreaLinkage1',
    colProps: {
      span: 12,
    },
    defaultValue: ['130000', '130200'],
  },

  {
    field: 'field0',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'field2',
    component: 'JAreaLinkage',
    label: '禁用组件(方式二)',
    helpMessage: ['component模式'],
    colProps: {
      span: 12,
    },
    dynamicDisabled: ({ values }) => {
      console.log(values);
      return isDisabledAuth(['demo.dbarray']);
    },
    defaultValue: ['140000', '140300', '140302'],
  },
  {
    field: 'field2',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'pca1',
    component: 'JAreaSelect',
    label: '省市区级联',
    helpMessage: ['component模式'],
    defaultValue: '140302',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'pca1',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'pop1',
    component: 'Input',
    label: 'JPopup示例',
    helpMessage: ['插槽模式'],
    slot: 'JPopup',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'pop1',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'JInputPop',
    component: 'JInputPop',
    label: 'JInputPop',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
  },
  {
    field: 'JInputPop',
    component: 'JEllipsis',
    label: '输入值',
    colProps: { span: 12 },
  },
  {
    field: 'JTreeDictAsync',
    component: 'JTreeDict',
    label: '异步JTreeDict',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
    componentProps: { async: true },
  },
  {
    field: 'JTreeDictAsync',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'JSwitch',
    component: 'JSwitch',
    label: 'JSwitch',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
  },
  {
    field: 'JSwitch',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'JSwitchSelect',
    component: 'JSwitch',
    label: 'JSwitchSelect',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
    componentProps: { query: true },
  },
  {
    field: 'JSwitchSelect',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  
  {
    field: 'userSelect2',
    component: 'UserSelect',
    label: '高级用户选择',
    helpMessage: ['component模式'],
    colProps: { span: 12 },
  },
  {
    field: 'userSelect2',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  
  {
    field: 'superQuery',
    component: 'Input',
    label: '高级查询',
    helpMessage: ['插槽模式'],
    slot: 'superQuery',
    colProps: { span: 12 },
  },
  {
    field: 'superQuery',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'pop2',
    component: 'JPopupDict',
    label: 'JPopupDict示例',
    colProps: {
      span: 12,
    },
    componentProps:{
      placeholder: '请选择',
      dictCode: 'report_user,username,id',
      multi: true,
    },
  },
  {
    field: 'pop2',
    component: 'JEllipsis',
    label: '选中值',
    colProps: {
      span: 12,
    },
  },
  {
    field: 'sex',
    component: 'JDictSelectTag',
    label: '性别(控制下方课程options)',
    helpMessage: ['component模式','性别不同,下方课程展示选项不同'],
    componentProps: {
      dictCode: 'sex',
      type: 'radioButton',
      onChange: (value) => {
        console.log(value);
      },
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'sex',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
  {
    field: 'course',
    component: 'Select',
    label: '课程',
    dynamicPropskey: 'options',
    dynamicPropsVal: ({ model }) => {
      let options;
      if (model.sex == 1) {
        return [
          { value: '0', label: 'java - 男' },
          { value: '1', label: 'vue - 男' },
        ];
      } else if (model.sex == 2) {
        return [
          { value: '2', label: '瑜伽 - 女' },
          { value: '3', label: '美甲 - 女' },
        ];
      } else {
        return [];
      }
    },
    componentProps: {
      disabled: false,
    },
    colProps: {
      span: 12,
    },
  },
  {
    field: 'course',
    component: 'JEllipsis',
    label: '选中值',
    colProps: { span: 12 },
  },
];

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果你想根据组件的 props 属性来动态加载组件,你可以使用条件渲染和动态导入(Dynamic Import)来实现。 假设你有一个名为 `ComponentLoader` 的组件,它接收一个名为 `componentName` 的 props 属性,根据这个属性来动态加载对应的组件。 ```tsx import React, { lazy, Suspense } from 'react'; interface ComponentLoaderProps { componentName: string; } const ComponentLoader: React.FC<ComponentLoaderProps> = ({ componentName }) => { // 根据传入的组件名称动态加载对应的组件 const Component = lazy(() => import(`./components/${componentName}`)); return ( <Suspense fallback={<div>Loading...</div>}> <Component /> </Suspense> ); }; export default ComponentLoader; ``` 在上述示例中,我们使用 `lazy` 和动态导入来根据传入的 `componentName` 属性值动态加载对应的组件。然后,我们使用 `Suspense` 组件提供的加载中状态处理。 在使用 `ComponentLoader` 组件时,你可以通过传递不同的 `componentName` 属性值来加载不同的组件: ```tsx import React from 'react'; import ComponentLoader from './ComponentLoader'; const App: React.FC = () => { return ( <div> <h1>My React App</h1> <ComponentLoader componentName="Home" /> <ComponentLoader componentName="About" /> </div> ); }; export default App; ``` 在上述示例中,我们通过给 `ComponentLoader` 组件传递不同的 `componentName` 属性值来动态加载不同的组件。 请确保组件名称的正确性,并根据需要进行错误处理和加载状态的处理。 希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值