//@ts-nocheck import { computed, defineComponent, isProxy, toRaw } from "vue"; import renders from "./itemRenders"; import { checkXItem, EVENT_TYPE, TIPS_TYPE } from "../tools/validate"; import { xU } from "../ventoseUtils"; import { diff } from "jsondiffpatch"; import { State_UI } from "../State_UI"; const { MutatingProps } = xU; const domClass = { tipsError: "ant-form-item-explain ant-form-item-explain-error" }; const devHelper = {}; const WILL_DELETE = [ "onValidateForm", "_$updateUI", "once", "itemTips", "rules", "labelVNodeRender", "slots", "validate", /* value 用updateValue处理,该值会触发render */ "value" ]; /* itemWrapperClass */ export const xItem = defineComponent({ name: "XItem", props: { /* 绑定的值 */ modelValue: { type: [Object, String, Number, Boolean], default: undefined }, configs: { type: Object, default() { return {}; } } }, emits: ["update:modelValue"], setup(props, { attrs, slots, emit, expose }) { let Cpt_isShowXItem: any = true; let Cpt_isDisabled: any = false; /*isShow*/ if (xU.isFunction(props.configs.isShow)) { Cpt_isShowXItem = computed(props.configs.isShow); } else if (xU.isBoolean(props.configs.isShow)) { Cpt_isShowXItem = computed(() => !!props.configs.isShow); } else { /* isShow 可能不存在 */ Cpt_isShowXItem = computed(() => { if (xU.isUndefined(props.configs.isShow)) { props.configs.isShow = true; } return !!props.configs.isShow; }); } /*disabled*/ if (xU.isFunction(props.configs.disabled)) { Cpt_isDisabled = computed(props.configs.disabled); } else if (xU.isBoolean(props.configs.disabled)) { Cpt_isDisabled = computed(() => !!props.configs.disabled); } /*readonly 在configs中,各个render自行实现*/ return { Cpt_isShowXItem, Cpt_isDisabled }; }, data(vm) { const { $props, $attrs } = vm; const triggerValidate = xU.debounce(function (eventType: string) { const { configs } = vm.$props; /*validate的定义 搜索 MutatingProps_configs_validate */ if (configs.validate) { configs.validate({ eventType: eventType, value: vm.properties.value }); } }, 500); const { listeners, propsWillDeleteFromConfigs } = (() => { const { configs } = $props; /* 后面的属性覆盖前面的属性 */ /* propsWillDeleteFromConfigsSet jsx 避免 listener 与 properties 实践名称重复 */ const propsSet = new Set(); /* 需要一个事件分发,拦截所有事件,再根据配置信息 */ const listeners = { /* 主要的触发方式 */ "onUpdate:value": (val: any) => { /* 使用configs.value的形式,一般是configs与组件是一对一的关系,configs需要是reactive的 */ if (configs.value !== undefined) { if (configs.value === val) { return; } else { configs.value = val; } } /* 双向绑定 */ vm.$emit("update:modelValue", val); /* @ts-ignore */ if (xU.isFunction(listeners.onAfterValueEmit)) { /* @ts-ignore */ listeners.onAfterValueEmit(val); } /* TODO: rule检测*/ triggerValidate(EVENT_TYPE.update); }, onValidateForm: () => { triggerValidate(EVENT_TYPE.validateForm); }, onChange: () => { triggerValidate(EVENT_TYPE.change); }, onInput: () => { triggerValidate(EVENT_TYPE.input); }, onBlur: () => { triggerValidate(EVENT_TYPE.blur); }, onFocus: () => { triggerValidate(EVENT_TYPE.focus); } }; function makeEventHandlerSupportMultiple( prop: unknown, xItemInnerEventHandler: unknown ) { propsSet.add(prop); if ( typeof listeners[prop] === "object" && xU.isArray(listeners[prop].handlerArray) ) { listeners[prop].handlerArray.push(xItemInnerEventHandler); } else { listeners[prop] = (...args: any) => { xU.each(listeners[prop].handlerArray, listener => { listener?.apply(configs, args); }); }; listeners[prop].handlerArray = [xItemInnerEventHandler]; } } /* listenners默认都是EventHandler */ xU.each(listeners, (value, prop) => makeEventHandlerSupportMultiple(prop, value) ); xU.each(configs, (value, prop) => { /* FIX: 监听函数单独出来。listener不知道在哪里被覆盖了,inputPassword 被 pop 包裹,childListener被修改了,UI库??*/ if (xU.isListener(prop)) { makeEventHandlerSupportMultiple(prop, value); } }); xU.each($attrs, (value, prop) => { /* FIX: 监听函数单独出来。listener不知道在哪里被覆盖了,inputPassword 被 pop 包裹,childListener被修改了,UI库??*/ if (xU.isListener(prop)) { makeEventHandlerSupportMultiple(prop, value); } }); return { listeners, propsWillDeleteFromConfigs: [...propsSet] }; })(); return { /* 因修改configs属性,触发的UI刷新 */ rerenderCount: 0, /* validateInfo */ isRequired: false, /* validateInfo */ properties: null, itemSlots: {}, listeners, propsWillDeleteFromConfigs }; }, computed: { CurrentXItem() { if (xU.isObject(this.configs.itemType)) { if (isProxy(this.configs.itemType)) { return toRaw(this.configs.itemType); } return this.configs.itemType; } /* String */ return renders[this.configs.itemType] || renders.Input; }, itemTypeName() { if (xU.isString(this.configs.itemType)) { return String(this.configs.itemType); } return ""; }, isChecking() { return Boolean(this.configs.checking); }, /* 组件唯一标识 */ FormItemId() { return `xItem_${this._.uid}`; }, /* 提示信息的类型及提示信息 */ itemTips() { const _itemTips = { type: "", msg: "" }; if (this.configs?.itemTips?.type) { return { type: this.configs.itemTips.type, msg: xU.isFunction(this.configs.itemTips.msg) ? this.configs.itemTips.msg() : this.configs.itemTips.msg }; } else { this.configs.itemTips = _itemTips; return _itemTips; } }, itemWrapperClass() { return [ this.configs.itemWrapperClass, /*flex 一般与从简在同一行*/ "ant-form-item ant-form-item-with-help x-item flex", this.itemTips.type === TIPS_TYPE.error ? "ant-form-item-has-error" : "" ].join(" "); }, /* VNode */ tipsVNode() { if (this.isChecking) { return (
checking...
); } if (this.configs.tipsVNodeRender) { return this.configs.tipsVNodeRender({ xItem: this, configs: this.configs, itemTips: this.itemTips }); } if (this.itemTips.msg) { if (this.itemTips.type === TIPS_TYPE.error) { return (
{this.itemTips.msg}
); } } return null; }, /* 表单label 如果有提供String类型,就显示 */ labelVNode() { const classString = this.isRequired ? "ant-form-item-required" : ""; if (this.configs.labelVNodeRender) { return this.configs.labelVNodeRender(this.configs, classString); } let label = (() => { const _label = this.configs.label; if (_label) { if (xU.isFunction(_label)) { return _label(); } if (xU.isString(_label) || _label.__v_isVNode) { return _label; } } return false; })(); if (label === false) { return null; } return (
); } /* VNode */ }, watch: { $attrs: { handler() { this.setProperties(); } }, "properties.value": { handler(value) { xU(value); } }, "configs.value": { handler() { this.updateValue(); } }, modelValue: { handler() { this.updateValue(); } }, rerenderCount: { handler() { this.setProperties(); } }, configs: { handler() { this.setProperties(); } }, "configs.options": { handler(value) { this.setProperties(); } }, /* 修改rules Array 要求全量替换 */ "configs.rules": { immediate: true, handler(rules) { if (rules) { this.setValidateInfo(rules); } } }, "configs.slots": { immediate: true, handler(slots) { if (slots) { this.setItemSlots(); } } } }, created() { const vm = this; vm.configs.FormItemId = vm.FormItemId; /*[似乎] Vue3 用的是类方法不是实例方法, (用 debounce,未跟实例绑定,不同实例调用同一个方法只会执行最后一个)*/ (() => { vm.forceUpdateUI = xU.debounce(() => vm.rerenderCount++, 64); })(); (() => { vm.configs._$updateUI = newConfigs => { xU.each(newConfigs, (value, prop) => { vm.configs[prop] = value; }); vm.forceUpdateUI(); }; })(); (() => { vm.updateValue = xU.debounce(vm.updateValueSync, 94); // vm.updateValue = vm.updateValueSync; vm.updateValue(); })(); (() => { vm.setProperties = xU.debounce(function setProperties(this: any) { /* @ts-ignore */ xU(vm._.uid); const __properties = {}; const pickProps = (originConfigs: any) => { xU.each(originConfigs, (item, prop) => { /* 用于xForm 控件,以下配置信息跟UI库控件相关,用不上,遂删除 */ if (WILL_DELETE.includes(prop)) { return; } if (["placeholder"].includes(prop) && xU.isFunction(item)) { __properties[prop] = item(this); return; } __properties[prop] = item; }); }; xU.each([this.configs, this.$attrs], pickProps); this.properties = __properties; this.updateValue(); }, 32); vm.setProperties(); })(); }, mounted() { devHelper[this._.uid] = 0; if (this.configs?.once) { this.configs.once.call(this.configs, this); } State_UI.xItemCollection[this.FormItemId] = this; }, beforeUnmount() { delete devHelper[this._.uid]; delete State_UI.xItemCollection[this.FormItemId]; }, methods: { async updateValueSync() { await xU.ensureValueDone(() => this.properties); const vm = this; /* modelValue configs.value configs.defaultValue */ const value = (() => { if (vm.modelValue !== undefined) { return vm.modelValue; } if (vm.configs.value == undefined) { if (vm.configs.defaultValue !== undefined) { return vm.configs.defaultValue; } else { xU("either configs.value or modelValue"); } } return vm.configs.value; })(); const diffRes = diff(vm.properties.value, value); if (diffRes) { xU("diff xItem value", diffRes); vm.properties.value = value; vm.listeners["onUpdate:value"](value); } }, setTips(type = "", msg = "") { MutatingProps(this, "configs.itemTips", { type, msg }); }, setItemSlots() { this.itemSlots = this.configs.slots || {}; }, /* 如果有可用rules,为当前item配置校验函数 */ setValidateInfo(rules: any) { /* 修改rules Array 要求全量替换 */ let isRequired = false; if (xU.isArrayFill(rules)) { /* 如果有必填项 */ isRequired = xU.some(rules, { name: "required" }); /* 检测完成之后的回调 */ const fnCheckedCallback = ([prop, msg]) => { MutatingProps(this, "configs.checking", false); if (prop) { if (msg) { this.setTips(TIPS_TYPE.error, msg); /*校验未通过,如果有其他操作,可以提供一个onValidateFail的回调函数*/ if (xU.isFunction(this.configs.onValidateFail)) { this.configs.onValidateFail(this.configs); } } else { this.setTips(); } } }; const debounceCheckXItem = xU.debounce(checkXItem, 300); const fnConfigsValidate = ({ eventType, value, resolve }) => { /* 短时间内,多个事件触发同一校验,使用队列,只执行一次 */ const prop = `configs.validate.triggerEventsObj.${eventType}`; MutatingProps(this, prop, true); /* */ debounceCheckXItem({ FormItemId: this.FormItemId, xItemConfigs: this.configs, value, /* 异步回调 */ fnCheckedCallback, resolve }); }; /* 如果有检验规则,添加可执行校验方法 MutatingProps_configs_validate */ MutatingProps(this, "configs.validate", fnConfigsValidate); /* init */ MutatingProps(this, "configs.validate.triggerEventsObj", {}); } else { if (xU.isFunction(this.configs.validate)) { delete this.configs.validate; } } this.isRequired = isRequired; } }, render() { if (!this.properties) { return null; } if (xU.isUndefined(this.Cpt_isShowXItem)) { debugger; } if (!this.Cpt_isShowXItem) { return null; } const { CurrentXItem, properties, Cpt_isDisabled, propsWillDeleteFromConfigs, itemTypeName, FormItemId } = this; xU(`xItem ${this._.uid} render ${++devHelper[this._.uid]} times`); return (
{/* label */} {this.labelVNode} {/* 控件 */}
{/* 提示信息 */} {this.tipsVNode}
{this.$slots.afterControll && this.$slots.afterControll()}
); } });