diff --git a/.eslintrc.js b/.eslintrc.js index ae1ee675110f88f12d610a5cb5316e2c5e6e5cf0..8d07e9839e9a972d9c2a127a64cb9b20f040b11b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,6 +19,7 @@ module.exports = { 'quote-props': ['warn', 'as-needed'], 'comma-dangle': ['error', 'only-multiline'], camelcase: ['error', { properties: 'never' }], + 'array-bracket-spacing': 'warn', 'arrow-spacing': 'warn', 'block-spacing': 'warn', @@ -30,12 +31,22 @@ module.exports = { 'object-curly-spacing': ['warn', 'always'], 'rest-spread-spacing': 'warn', 'switch-colon-spacing': 'error', + 'func-call-spacing': 'warn', 'semi-spacing': 'warn', 'template-curly-spacing': 'warn', 'template-tag-spacing': 'warn', 'yield-star-spacing': 'warn', - semi: ['warn', 'always'], + 'space-unary-ops': 'warn', + 'no-multi-spaces': 'warn', + 'no-mixed-spaces-and-tabs': 'warn', 'no-trailing-spaces': 'warn', + 'spaced-comment': 'warn', + 'space-infix-ops': 'warn', + + 'eol-last': 'warn', + indent: ['warn', 2], + + semi: ['warn', 'always'], 'prefer-template': 'error', 'prefer-spread': 'error', 'no-var': 'error', @@ -81,4 +92,4 @@ module.exports = { } ], }, -}; \ No newline at end of file +}; diff --git a/.vscode/settings.json b/.vscode/settings.json index b8b8dd50c04e72d3e1a477ef77bcfe8729842f61..89532e0246e2e7bea74ada6738a1d7e688bef0b0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,12 +3,15 @@ "explorer.fileNesting.patterns": { "package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml,pnpm-workspace.yaml,.npmrc,.gitignore,LICENSE.md", ".eslintrc.js": ".prettierrc.js", - "README.md": "README.en.md", + "README.md": "README.en.md" }, "outline.showFiles": true, "files.exclude": { "packages/opendesign/dist": true, "packages/opendesign/es": true, "packages/opendesign/lib": true + }, + "editor.codeActionsOnSave": { + "source.fixAll": "explicit" } -} \ No newline at end of file +} diff --git a/packages/opendesign/src/_components/in-input/__demo__/TheIndex.vue b/packages/opendesign/src/_components/in-input/__demo__/TheIndex.vue index c406ad8b9068be7b4fa037b6c58911cb27b32dac..9e058221aea850e881f94ab885a3074b32b62cd7 100644 --- a/packages/opendesign/src/_components/in-input/__demo__/TheIndex.vue +++ b/packages/opendesign/src/_components/in-input/__demo__/TheIndex.vue @@ -3,7 +3,7 @@ import '../style'; import InInput from '../InInput.vue'; import { ref } from 'vue'; -const inputVal = ref('124567890'); +const inputVal = ref('124567123'); const printEvent = (evt: string, v?: string) => { console.log(`[${evt}]`, v ?? '', 'inputVal:', inputVal.value); @@ -11,7 +11,7 @@ const printEvent = (evt: string, v?: string) => { const disabled = ref(false); const maxLength = ref(6); -const minLength = ref(4); +const minLength = ref(2); const toggle = () => { disabled.value = !disabled.value; maxLength.value = 5; @@ -29,6 +29,7 @@ const validate = (value: string): boolean => { const onUpdate = (val: string) => { inputVal.value = val; + console.log('onUpdate', val); }; const format = (val: string) => { @@ -37,7 +38,7 @@ const format = (val: string) => { const valueOnInvalidChange = (currentValue: string, lastValid: string) => { console.log('valueOnInvalidChange:', currentValue, lastValid); - return lastValid; + return lastValid || currentValue; }; const onChange = (currentValue: string, lastValue: string) => { @@ -67,7 +68,7 @@ window.setInterval(() => { @clear="() => printEvent('clear')" @blur="() => printEvent('blur')" @change="onChange" - @input="(e, value) => printEvent('input', value)" + @input="(e:Event, value:string) => printEvent('input', value)" @focus="() => printEvent('focus')" @press-enter="() => printEvent('press-enter')" clearable @@ -87,8 +88,8 @@ window.setInterval(() => { :validate="validate" @clear="() => printEvent('clear')" @blur="() => printEvent('blur')" - @change="(v) => printEvent('change', v)" - @input="() => printEvent('input')" + @change="(v:string) => printEvent('change', v)" + @input="(_e:Event, v:string) => printEvent('input', v)" @focus="() => printEvent('focus')" @press-enter="() => printEvent('press-enter')" clearable diff --git a/packages/opendesign/src/_components/in-input/types.ts b/packages/opendesign/src/_components/in-input/types.ts index 10df898927c5bd3a017b21c5ffebb46e1e71b6ab..110183304031cddb1a9ddb64a760e0848419cff5 100644 --- a/packages/opendesign/src/_components/in-input/types.ts +++ b/packages/opendesign/src/_components/in-input/types.ts @@ -81,7 +81,7 @@ export const inInputProps = { type: Function as PropType<(val: string) => number>, }, /** - * @zh-CN 超过最大字符长度时是否允许输入 + * @zh-CN 超过最大字符长度时是否允许输入,当为false时,输入长度超出maxLength会被截断 * @en-US Whether input is allowed when the maximum character length is exceeded. * @default true */ @@ -104,11 +104,11 @@ export const inInputProps = { type: Function as PropType<(value: string) => boolean>, }, /** - * @zh-CN 输入为无效值时,在blur/pressEnter时的回调,返回值为纠正后的值 + * @zh-CN 输入为无效值时,在blur/pressEnter时的回调,返回值为纠正后的值;当输入值不合法时的处理方式:[true]:纠正为上一次合法的值(如果上一次合法值为空字符串,则不处理); [false|undefined]: 不处理;[function]: 使用函数的返回值 * @en-US When the input value is an invalid value, the callback during blur/pressEnter returns the corrected value. */ valueOnInvalidChange: { - type: Function as PropType<(inputValue: string, lastValidInputValue: string) => string>, + type: [Boolean, Function as PropType<(inputValue: string, lastValidInputValue: string) => string>], }, /** * @zh-CN 显示密码的方式 diff --git a/packages/opendesign/src/_components/in-textarea/types.ts b/packages/opendesign/src/_components/in-textarea/types.ts index 69b5bd3e3fdebcd8234fc0ff7624d5b71099d0b2..2df34eae8abfd634db5b56a2e1f33bf2ca0ba2f8 100644 --- a/packages/opendesign/src/_components/in-textarea/types.ts +++ b/packages/opendesign/src/_components/in-textarea/types.ts @@ -62,11 +62,11 @@ export const inTextareaProps = { type: Function as PropType<(value: string) => boolean>, }, /** - * @zh-CN 输入非法值时的回调 - * @en-US Callback when input value is invalid + * @zh-CN 输入为无效值时,在blur/pressEnter时的回调,返回值为纠正后的值;当输入值不合法时的处理方式:[true]:纠正为上一次合法的值(如果上一次合法值为空字符串,则不处理); [false|undefined]: 不处理;[function]: 使用函数的返回值 + * @en-US When the input value is an invalid value, the callback during blur/pressEnter returns the corrected value. */ valueOnInvalidChange: { - type: Function as PropType<(inputValue: string, lastValidInputValue: string) => string>, + type: [Boolean, Function as PropType<(inputValue: string, lastValidInputValue: string) => string>], }, /** * @zh-CN 同 textarea 的 rows 属性 diff --git a/packages/opendesign/src/_headless/use-input.ts b/packages/opendesign/src/_headless/use-input.ts index 5c62559875b37c2d09d6ec624af4f5e1b328c606..9a2d2b9798f48e38dba5491e5207739d5a959f14 100644 --- a/packages/opendesign/src/_headless/use-input.ts +++ b/packages/opendesign/src/_headless/use-input.ts @@ -7,7 +7,7 @@ import { ref, computed, Ref, watch, nextTick } from 'vue'; export type UseInputEmitsT = { // 仅在输入框失焦或按下回车时触发 (e: 'change', value: string, lastValue: string): void; - // 用户输入时触发 + // 用户输入时(键盘输入、粘贴等)触发,value为当前输入的值 (e: 'input', evt: Event, value: string): void; // 输入框获取焦点时触发 (e: 'focus', evt: FocusEvent): void; @@ -24,7 +24,8 @@ export interface InputOptionT { emits: UseInputEmitsT; emitUpdate: (value: string) => void; validate?: (value: string) => boolean; - valueOnInvalidChange?: (inputValue: string, lastValidInputValue: string) => string; + // 当输入值不合法时的处理方式:[true]:纠正为上一次合法的值(如果上一次合法值为空字符串,则不处理); [false|undefined]: 不处理;[function]: 使用函数的返回值 + valueOnInvalidChange?: boolean | ((inputValue: string, lastValidInputValue: string) => string); format?: (value: string) => string; maxLength?: Ref; minLength?: Ref; @@ -46,13 +47,13 @@ export function useInput(options: InputOptionT) { return isFunction(calculateLength) ? calculateLength(v) : v?.length; }; - const uncontroledValue = ref(defaultValue); - const controledValue = modelValue; + const uncontrolledValue = ref(defaultValue); + const controlledValue = modelValue; // 当前值 const computedValue = computed(() => { - const cv = controledValue?.value; - const ucv = uncontroledValue.value ?? ''; + const cv = controlledValue?.value; + const ucv = uncontrolledValue.value ?? ''; return cv ?? ucv; }); @@ -109,7 +110,7 @@ export function useInput(options: InputOptionT) { // 值可用状态 const isValid = ref(true); - /**` + /** * 校验是否值有效,如果值为空,始终有效 */ const validateValue = (value: string) => { @@ -144,9 +145,12 @@ export function useInput(options: InputOptionT) { ); const updateValue = (value: string) => { - uncontroledValue.value = value; + uncontrolledValue.value = value; - emitUpdate(value); + // 判断值是否变化,有变化再触发事件 + if (value !== computedValue.value) { + emitUpdate(value); + } }; const getValidValue = () => { @@ -154,10 +158,10 @@ export function useInput(options: InputOptionT) { // 值有效性校验 if (!isValid.value) { if (isFunction(valueOnInvalidChange)) { - // 这调用valueOnInvalidChange回调获取对应回调值 + // 调用valueOnInvalidChange回调获取对应回调值 validVal = valueOnInvalidChange(computedValue.value, lastValidValue); validateValue(validVal); - } else { + } else if (valueOnInvalidChange === true && lastValidValue !== ''){ // 回退到上一次有效值 validVal = lastValidValue; isValid.value = true; @@ -169,8 +173,10 @@ export function useInput(options: InputOptionT) { const emitChange = (value: string) => { if (value !== lastValue) { - emits('change', computedValue.value, lastValue); - lastValue = computedValue.value; + nextTick(() => { + emits('change', computedValue.value, lastValue); + lastValue = computedValue.value; + }); } }; @@ -181,15 +187,18 @@ export function useInput(options: InputOptionT) { } }; - const isAllowedToInput = (value: string) => { - if (inputOnOutlimit?.value) { + const isAllowedToInputOnOutLimit = (value: string) => { + // 未设置最大长度或者允许查出最大长度后可继续输入 + if (!isUndefined(maxLength?.value) && inputOnOutlimit?.value === true) { return true; } + const len = calculateStringLength(value); const isLower = validateMaxLength(len); if (isLower) { return true; } + // 超出长度限制,且为字符长度减少,则支持操作 if (len < calculateStringLength(computedValue.value)) { return true; @@ -199,18 +208,25 @@ export function useInput(options: InputOptionT) { const handleInput = (e: Event) => { const value = (e.target as HTMLInputElement)?.value; + if (composition.isComposing.value) { // 解决在输入中文时,组件触发onUpdate时,显示值被刷新成输入前的值 displayValue.value = value; return; } - if (isAllowedToInput(value)) { - updateValue(value); + // 始终上报当前输入的值,可能经过校验、或截断后显示的值与输入的不一致 + emits('input', e, value); + + let newValue = value; - emits('input', e, value); + if (!isAllowedToInputOnOutLimit(value)) { + // 当超出长度限制不允许输入时,按照最大长度截断 + newValue = value.substring(0, maxLength?.value); } + updateValue(newValue); + nextTick(() => { keepNativeDisplayValue(); });