Appearance
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:
- k: alias
- payload: 组件库的组件原生事件参数 例:Select 选择器的 select 事件的参数说明
- $listeners
- suffix: 事件后缀,用于区分事件类型,如 select、change
- k: alias