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/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 @@
+
+ 验证码输入框
+ 验证码: 1234
+
+
+
+
+
+ {{ sendLabel }}
+
+
+
+
验证通过
+
+
+
+
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';
+