From 19362fadf73f835195ff50533288ad278cce9019 Mon Sep 17 00:00:00 2001 From: sakurayinfei <970412446@qq.com> Date: Mon, 28 Apr 2025 10:51:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DOInput=E5=8F=8AOTextar?= =?UTF-8?q?ea=E5=9C=A8=E5=8F=91=E9=80=81=E9=AA=8C=E8=AF=81=E7=A0=81?= =?UTF-8?q?=E8=AF=BB=E7=A7=92=E7=AD=89=E5=9C=BA=E6=99=AF=E4=B8=AD=EF=BC=8C?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8=E8=BE=93=E5=85=A5=E6=B3=95?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E5=86=85=E5=AE=B9=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/_components/in-input/InInput.vue | 10 +- .../_components/in-textarea/InTextarea.vue | 4 +- packages/opendesign/src/directives/index.ts | 1 + packages/opendesign/src/directives/o-model.ts | 25 +++++ .../opendesign/src/hooks/use-composition.ts | 8 +- .../src/input/__demo__/InputVerification.vue | 15 +-- .../__demo__/TextareaVerification.vue | 94 +++++++++++++++++++ .../src/textarea/__demo__/TheIndex.vue | 2 + 8 files changed, 144 insertions(+), 15 deletions(-) create mode 100644 packages/opendesign/src/directives/o-model.ts create mode 100644 packages/opendesign/src/textarea/__demo__/TextareaVerification.vue diff --git a/packages/opendesign/src/_components/in-input/InInput.vue b/packages/opendesign/src/_components/in-input/InInput.vue index 61ccfc54..e2f68eeb 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 bcfc6fc6..ec5fed01 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 d92e7281..4bdb6a03 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 00000000..d987b67c --- /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 a91c78e0..9500de3a 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 874104ef..3d064453 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 68af36de..5af2dba3 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'; -- Gitee