diff --git a/packages/opendesign/src/_components/in-input/InInput.vue b/packages/opendesign/src/_components/in-input/InInput.vue index 61ccfc544dfa3348ede76c5a1ca162a9f998d4b7..e2f68eebce22e4941b4cf2b6bb28dc8c71cb9416 100644 --- a/packages/opendesign/src/_components/in-input/InInput.vue +++ b/packages/opendesign/src/_components/in-input/InInput.vue @@ -5,6 +5,7 @@ import { IconClose, IconEyeOn, IconEyeOff } from '../../_utils/icons'; import { useInput, type UseInputEmitsT } from '../../_headless/use-input'; import { useInputPassword } from '../../_headless/use-input-password'; import { useI18n } from '../../locale'; +import { vOModel } from '../../directives'; const props = defineProps(inInputProps); const slots = defineSlots<{ @@ -110,18 +111,23 @@ defineExpose({
+
diff --git a/packages/opendesign/src/_components/in-textarea/InTextarea.vue b/packages/opendesign/src/_components/in-textarea/InTextarea.vue index bcfc6fc61d7617ccfb73d78776bb25d7491d82af..ec5fed01bd943c50f922400ec9a98062b5af28af 100644 --- a/packages/opendesign/src/_components/in-textarea/InTextarea.vue +++ b/packages/opendesign/src/_components/in-textarea/InTextarea.vue @@ -5,6 +5,7 @@ import { IconClose } from '../../_utils/icons'; import { useInput } from '../../_headless/use-input'; import { useI18n } from '../../locale'; import { vScrollbar } from '../../scrollbar'; +import { vOModel } from '../../directives'; const props = defineProps(inTextareaProps); @@ -124,7 +125,7 @@ defineExpose({
diff --git a/packages/opendesign/src/directives/index.ts b/packages/opendesign/src/directives/index.ts index d92e7281d6a564a7a23874def074a9b380c79459..4bdb6a036028fcd598d06392970508b76b969e7d 100644 --- a/packages/opendesign/src/directives/index.ts +++ b/packages/opendesign/src/directives/index.ts @@ -3,3 +3,4 @@ export * from './focus'; export * from './on-intersection'; export * from './on-resize'; export * from './uid'; +export * from './o-model'; diff --git a/packages/opendesign/src/directives/o-model.ts b/packages/opendesign/src/directives/o-model.ts new file mode 100644 index 0000000000000000000000000000000000000000..d987b67c4721f94ce47e4f894ef59633885fd005 --- /dev/null +++ b/packages/opendesign/src/directives/o-model.ts @@ -0,0 +1,25 @@ +import { type ObjectDirective } from 'vue'; + +export const vOModel: ObjectDirective = { + created(el, { value }) { + const { handleInput } = value; + if (typeof handleInput !== 'function') { + throw new Error('v-o-model: handleInput is not a function'); + } + el.addEventListener('input', handleInput); + }, + mounted(el, { value }) { + const { modelValue } = value; + el.value = modelValue == null ? '' : modelValue; + }, + beforeUpdate(el, { value }) { + if ((el as any).composing) { + return; + } + const { modelValue } = value; + if (el.value === modelValue) { + return; + } + el.value = modelValue == null ? '' : modelValue; + }, +}; diff --git a/packages/opendesign/src/hooks/use-composition.ts b/packages/opendesign/src/hooks/use-composition.ts index a91c78e033ac5b084dfe7c4cf6b68b0a5c2f0660..9500de3a5c42e2746143f3d19b22246b39173f4c 100644 --- a/packages/opendesign/src/hooks/use-composition.ts +++ b/packages/opendesign/src/hooks/use-composition.ts @@ -5,8 +5,10 @@ export function useComposition({ el }: { el?: Ref } = { const isComposing = ref(false); // 开始中文输入 - const onCompositionStart = () => { + const onCompositionStart = (e: Event) => { isComposing.value = true; + // e.target.composing 会在 vOModel 中使用 + (e.target as any).composing = true; }; // 结束中文输入 const onCompositionEnd = (e: Event) => { @@ -15,6 +17,7 @@ export function useComposition({ el }: { el?: Ref } = { } isComposing.value = false; + (e.target as any).composing = false; trigger(e.target as HTMLElement, 'input'); }; @@ -24,6 +27,9 @@ export function useComposition({ el }: { el?: Ref } = { } el.value.addEventListener('compositionstart', onCompositionStart); el.value.addEventListener('compositionend', onCompositionEnd); + // Safari < 10.2 & UIWebView 在确认输入法选项前切换焦点时不会触发 compositionend 事件; + // 同时解决了某些浏览器(如 iOS Chrome)在自动填充时触发"change"事件而非"input"事件的问题。 + el.value.addEventListener('change', onCompositionEnd); }); onUnmounted(() => { diff --git a/packages/opendesign/src/input/__demo__/InputVerification.vue b/packages/opendesign/src/input/__demo__/InputVerification.vue index 874104ef3b2c5d2282ed015b4470333fb83b9fb9..3d064453573dcad5072fac7afe358e87624ff390 100644 --- a/packages/opendesign/src/input/__demo__/InputVerification.vue +++ b/packages/opendesign/src/input/__demo__/InputVerification.vue @@ -1,9 +1,7 @@ + + diff --git a/packages/opendesign/src/textarea/__demo__/TheIndex.vue b/packages/opendesign/src/textarea/__demo__/TheIndex.vue index 68af36de137e9a51b3043c3a84d4711ad3bf1258..5af2dba351983b261f3e7d5f5b33036dbadbc403 100644 --- a/packages/opendesign/src/textarea/__demo__/TheIndex.vue +++ b/packages/opendesign/src/textarea/__demo__/TheIndex.vue @@ -5,12 +5,14 @@ import '../../option/style'; import TextareaBasic from './TextareaBasic.vue'; import TextareaAutoHeight from './TextareaAutoHeight.vue'; import TextareaSlot from './TextareaSlot.vue'; +import TextareaVerification from './TextareaVerification.vue';