Skip to content
On this page

template

vue
<script lang="jsx">
// import type { PropType } from 'vue'
// import { RulesConfig } from '../types'
import { defineComponent, unref, ref } from 'vue';
import { camelCase, kebabCase, capitalize } from 'lodash-es';

import GeneralForm from './GeneralForm';
import { useListeners } from '@/hooks/core/useListeners';
import { returnReferType } from '../utils';
import { extendSlots } from '@/hooks/core/useJsxHelper';
import { h } from 'vue';

function analysisEvent(k, payload, $listeners = {}, suffix = '') {
  const camelCaseK = `on${capitalize(k)}${capitalize(camelCase(suffix))}`;
  if ($listeners[camelCaseK]) {
    const _eventTrunk = `${kebabCase(k)}${suffix}`;
    return [_eventTrunk, payload];
  }
  return [];
}

export default defineComponent({
  name: 'HigherOrderForm',
  components: {
    GeneralForm,
  },
  inheritAttrs: false,
  props: {
    formProps: {
      type: Object,
      default: () => ({}),
    },
    formData: {
      type: Object,
      default: () => ({}),
    },
    rulesConfig: {
      // type: Object as unknown as PropType<RulesConfig>,
      type: Object,
      default: () => ({}),
    },
  },
  expose: ['validate'],
  setup(props, { slots, attrs, emit }) {
    const generalFormRef = ref();
    const popRef = ref();
    const $listeners = useListeners();
    console.log('$listeners', $listeners);
    // FIXME
    // const emit = defineEmits(Object.keys($listeners).map((item) => kebabCase(item.alias)))

    const onUpdateValue = (k, e) => {
      const ret = analysisEvent(k, e, $listeners);
      // console.log('[HOC onUpdateValue]', ret)
      ret.length && emit(...ret);
    };

    const onStep = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-step');
      // console.log('[HOC onStep]', ret)
      ret.length && emit(...ret);
    };

    const onFocus = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-focus');
      // console.log('[HOC onFocus]', ret)
      ret.length && emit(...ret);
    };

    const onBlur = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-blur');
      // console.log('[HOC onBlur]', ret)
      ret.length && emit(...ret);
    };

    const onClear = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-clear');
      // console.log('[HOC onClear]', ret)
      ret.length && emit(...ret);
    };

    const onClick = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-click');
      // console.log('[HOC onClick]', ret)
      ret.length && emit(...ret);
    };

    const onUpdateChecked = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners);
      // console.log('[HOC onUpdateChecked]', ret)
      ret.length && emit(...ret);
    };

    const onChange = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-change');
      // console.log('[HOC onChange]', ret)
      ret.length && emit(...ret);
    };

    const onPressEnter = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-press-enter');
      // console.log('[HOC onPressEnter]', ret)
      ret.length && emit(...ret);
    };

    const onKeyUp = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-kep-up');
      // console.log('[HOC onKeyUp]', ret)
      ret.length && emit(...ret);
    };

    const onSearch = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-search');
      // console.log('[HOC onSearch]', ret)
      ret.length && emit(...ret);
    };

    const onDeselect = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-deselect');
      // console.log('[HOC onDeselect]', ret)
      ret.length && emit(...ret);
    };

    const onDropdownVisibleChange = (k, payload) => {
      const ret = analysisEvent(
        k,
        payload,
        $listeners,
        '-dropdown-visible-change'
      );
      // console.log('[HOC onDropdownVisibleChange]', ret)
      ret.length && emit(...ret);
    };

    const onInputKeyDown = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-input-key-down');
      // console.log('[HOC onInputKeyDown]', ret)
      ret.length && emit(...ret);
    };

    const onMouseenter = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-mouseenter');
      // console.log('[HOC onMouseenter]', ret)
      ret.length && emit(...ret);
    };

    const onMouseleave = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-mouseleave');
      // console.log('[HOC onMouseleave]', ret)
      ret.length && emit(...ret);
    };

    const onPopupScroll = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-popup-scroll');
      // console.log('[HOC onPopupScroll]', ret)
      ret.length && emit(...ret);
    };

    const onSelect = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-select');
      // console.log('[HOC onSelect]', ret)
      ret.length && emit(...ret);
    };

    const onOpenChange = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-open-change');
      // console.log('[HOC onOpenChange]', ret)
      ret.length && emit(...ret);
    };

    const onPanelChange = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-panel-change');
      // console.log('[HOC onPanelChange]', ret)
      ret.length && emit(...ret);
    };

    const onOk = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-ok');
      // console.log('[HOC onOk]', ret)
      ret.length && emit(...ret);
    };

    const onCalendarChange = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-calendar-change');
      // console.log('[HOC onCalendarChange]', ret)
      ret.length && emit(...ret);
    };

    const onDownload = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-download');
      // console.log('[HOC onDownload]', ret)
      ret.length && emit(...ret);
    };

    const onDrop = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-drop');
      // console.log('[HOC onDrop]', ret)
      ret.length && emit(...ret);
    };

    const onPreview = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-preview');
      // console.log('[HOC onPreview]', ret)
      ret.length && emit(...ret);
    };

    const onUploadReject = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-reject');
      // console.log('[HOC onReject]', ret)
      ret.length && emit(...ret);
    };

    const onRemove = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-remove');
      // console.log('[HOC onRemove]', ret)
      ret.length && emit(...ret);
    };

    const onScroll = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-scroll');
      // console.log('[HOC onScroll]', ret)
      ret.length && emit(...ret);
    };

    const onSelectChange = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-select-change');
      // console.log('[HOC onSelectChange]', ret)
      ret.length && emit(...ret);
    };

    const onCheck = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-check');
      // console.log('[HOC onCheck]', ret)
      ret.length && emit(...ret);
    };

    const onDragend = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-dragend');
      // console.log('[HOC onDragend]', ret)
      ret.length && emit(...ret);
    };

    const onDragenter = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-dragenter');
      // console.log('[HOC onDragenter]', ret)
      ret.length && emit(...ret);
    };

    const onDragleave = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-dragleave');
      // console.log('[HOC onDragleave]', ret)
      ret.length && emit(...ret);
    };

    const onDragover = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-dragover');
      // console.log('[HOC onDragover]', ret)
      ret.length && emit(...ret);
    };

    const onDragstart = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-dragstart');
      // console.log('[HOC onDragstart]', ret)
      ret.length && emit(...ret);
    };

    const onExpand = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-expand');
      // console.log('[HOC onExpand]', ret)
      ret.length && emit(...ret);
    };

    const onLoad = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-load');
      // console.log('[HOC onLoad]', ret)
      ret.length && emit(...ret);
    };

    const onRightClick = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-right-click');
      // console.log('[HOC onRightClick]', ret)
      ret.length && emit(...ret);
    };

    const onTreeExpand = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-tree-expand');
      // console.log('[HOC onTreeExpand]', ret)
      ret.length && emit(...ret);
    };

    const onHoverChange = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-hover-change');
      // console.log('[HOC onHoverChange]', ret)
      ret.length && emit(...ret);
    };

    const onKeydown = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-keydown');
      // console.log('[HOC onKeydown]', ret)
      ret.length && emit(...ret);
    };

    const onAfterChange = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-after-change');
      // console.log('[HOC onAfterChange]', ret)
      ret.length && emit(...ret);
    };

    const onImageError = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-error');
      // console.log('[HOC onError]', ret)
      ret.length && emit(...ret);
    };

    const onClose = (k, payload) => {
      const ret = analysisEvent(k, payload, $listeners, '-close');
      // console.log('[HOC onClose]', ret)
      ret.length && emit(...ret);
    };

    const validate = () => {
      console.log('validate', generalFormRef.value);
      return generalFormRef.value.validate();
    };

    function slotCompCtor(formProps) {
      const res = Object.create(null);
      const _slots = extendSlots(slots);

      function slotCompFitTogether(level) {
        if (level.widget?.toLowerCase() === 'slot') {
          res[level.alias] = () => slots[level.alias]?.();
        }

        const _bucket = level.bucket;
        if (!_bucket) return;

        if (returnReferType(_bucket) === 'Array' && _bucket.length) {
          for (let i = 0, length = _bucket.length; i < length; i++) {
            slotCompFitTogether(_bucket[i]);
          }
        } else if (returnReferType(_bucket) === 'Object')
          slotCompFitTogether(_bucket);
      }

      slotCompFitTogether(formProps);
      return res;
    }

    return () => {
      return (
        <div class="higher-order-form">
          <GeneralForm
            ref={generalFormRef}
            formProps={props.formProps}
            formData={props.formData}
            rulesConfig={props.rulesConfig}
            onGenerFocus={onFocus}
            onGenerBlur={onBlur}
            onGenerKeyUp={onKeyUp}
            onGenerClear={onClear}
            onGenerClick={onClick}
            onGenerUpdateValue={onUpdateValue}
            onGenerStep={onStep}
            onGenerUpdateChecked={onUpdateChecked}
            onGenerChange={onChange}
            onGenerPressEnter={onPressEnter}
            onGenerSearch={onSearch}
            onGenerDeselect={onDeselect}
            onGenerDropdownVisibleChange={onDropdownVisibleChange}
            onGenerInputKeyDown={onInputKeyDown}
            onGenerMouseenter={onMouseenter}
            onGenerMouseleave={onMouseleave}
            onGenerPopupScroll={onPopupScroll}
            onGenerSelect={onSelect}
            onGenerOpenChange={onOpenChange}
            onGenerPanelChange={onPanelChange}
            onGenerOk={onOk}
            onGenerCalendarChange={onCalendarChange}
            onGenerDownload={onDownload}
            onGenerDrop={onDrop}
            onGenerPreview={onPreview}
            onGenerReject={onUploadReject}
            onGenerRemove={onRemove}
            onGenerScroll={onScroll}
            onGenerSelectChange={onSelectChange}
            onGenerCheck={onCheck}
            onGenerDragend={onDragend}
            onGenerDragenter={onDragenter}
            onGenerDragleave={onDragleave}
            onGenerDragover={onDragover}
            onGenerDragstart={onDragstart}
            onGenerExpand={onExpand}
            onGenerLoad={onLoad}
            onGenerRightClick={onRightClick}
            onGenerTreeExpand={onTreeExpand}
            onGenerHoverChange={onHoverChange}
            onGenerKeydown={onKeydown}
            onGenerAfterChange={onAfterChange}
            onGenerError={onImageError}
            onGenerClose={onClose}
            v-slots={slotCompCtor(props.formProps)}
          />
        </div>
      );
    };
  },
});
</script>
<style scoped lang="less">
.higher-order-form {
}
</style>
  • @gener-*

    这里的事件是各个表单项原生事件,例: onChange; 在底层组件(GeneralForm)中处理加固定前缀发出的

  • slot

    具名插槽 name 是绑定的表单项的 alias, !!!: 待商榷

script 核心方法

js
import { useListeners } from '@/hooks/core/useListeners';

function analysisEvent(k, payload, $listeners = {}, suffix = '') {
  const camelCaseK = `on${capitalize(k)}${capitalize(camelCase(suffix))}`;

  if ($listeners[camelCaseK]) {
    const _eventTrunk = `${kebabCase(k)}${suffix}`;
    return [_eventTrunk, payload];
  }
  return [];
}

// 两个方法示例

const onSelect = (k, payload) => {
  const ret = analysisEvent(k, payload, $listeners, '-select');
  // console.log('[HOC onSelect]', ret)
  ret.length && emit(...ret);
};

const onUpdateValue = (k, e) => {
  const ret = analysisEvent(k, e, $listeners);
  // console.log('[HOC onUpdateValue]', ret)
  ret.length && emit(...ret);
};
  • slotCompCtor
js
function slotCompCtor(formProps) {
  const res = Object.create(null);
  const _slots = extendSlots(slots);

  function slotCompFitTogether(level) {
    if (level.widget?.toLowerCase() === 'slot') {
      res[level.alias] = () => slots[level.alias]?.();
    }

    const _bucket = level.bucket;
    if (!_bucket) return;

    if (returnReferType(_bucket) === 'Array' && _bucket.length) {
      for (let i = 0, length = _bucket.length; i < length; i++) {
        slotCompFitTogether(_bucket[i]);
      }
    } else if (returnReferType(_bucket) === 'Object')
      slotCompFitTogether(_bucket);
  }

  slotCompFitTogether(formProps);
  return res;
}

递归收集所有的 slot
v-slots jsx 写法 参考:

jsx
// 默认插槽
<MyComponent>{() => 'hello'}</MyComponent>

// 具名插槽
<MyComponent>{{
  default: () => 'default slot',
  foo: () => <div>foo</div>,
  bar: () => [<span>one</span>, <span>two</span>]
}}</MyComponent>
  • useListeners
js
import { getCurrentInstance } from 'vue';
import { isEventListener } from '@/utils/is';

export function useListeners() {
  const instance = getCurrentInstance();
  if (!instance) return {};

  const attrs = {};
  for (const [k, v] of Object.entries(instance.attrs)) {
    if (!isEventListener(k)) continue;
    attrs[k] = v;
  }
  return attrs;
}

获取当前组件的$attrs 筛选出事件类型
tips: 声名在调用 HigherOrderForm 组件上方法,例: onSelectname

  • analysisEvent

    作用:没有接收的事件在这里被拦截

    args: