@ desource/ phone- mask- vue
Vue 3 有 Google 的 libphone 数字数据的电话输入组件
美丽可及,极小可树可摇动的Vue 3手机输入,并带有自动格式化,国家选择器,以及验证功能.
粘贴完整的国际编号,首先是 + 检测其国家,并用数字取代输入,而不使用其调用代码。 检测使用掩码格式化,再用数字计数;模棱两可的匹配使用调用代码的默认国家. 没有调用代码的碎片保持了正常的插入行为. 这个 detect 选项分别控制 GeoIP 检测.
升级到 2. 0. 0 之前,审查 1.7.0的移徙 用于断开更改和需要更新.
* 特性
- 🌐 安保部门支助 —— 说
PhoneInput在服务器上使 HTML 格式化,并在浏览器中使水合物格式化 - 🎨 美丽的UI ——以轻/暗为主题的现代设计
- 🔍 智能国家搜索 ——模糊匹配键盘导航
- 🎭 自动格式化 ——有智能光标的现用类型格式
- ✅ 验证 ——内设有视觉反馈的验证
- 📋 复制按钮 — 一击副本到剪贴板
- 🌐 自动检测 ——地生化和地生化探测
- ♿ 可访问 ——ARIA标签,键盘导航
- 📱 便于移动 - 优化触摸装置
- 🎯 类型脚本 - 全套安全
- 🧩 三种模式 - 组件、可混合或指令
- ⚡ 优化 - 树摇和代码分割
* 安装
npm install @desource/phone-mask-vue
# or
yarn add @desource/phone-mask-vue
# or
pnpm add @desource/phone-mask-vue
QQ 快速启动
导入
组件模式 :
import { createApp } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css'; // Import styles
const app = createApp(App);
app.component('PhoneInput', PhoneInput);
app.mount('#app');
指令模式(用于自定义输入执行):
import { createApp } from 'vue';
import { vPhoneMask } from '@desource/phone-mask-vue';
const app = createApp(App);
app.directive('phone-mask', vPhoneMask);
app.mount('#app');
可编译模式(用于自定义输入执行,以防甚至指令都不符合您的需要):
import { usePhoneMask } from '@desource/phone-mask-vue';
从核心包子路径再出口的核心工具包和掩码数据:
import { getFlagEmoji, formatDigitsWithMap } from '@desource/phone-mask-vue/core';
全部同时注册( 组件+指令) :
import { createApp } from 'vue';
import phoneMask from '@desource/phone-mask-vue';
import '@desource/phone-mask-vue/assets/lib.css'; // Import styles for component mode
const app = createApp(App);
app.use(phoneMask); // Registers component and directive
app.mount('#app');
组件模式
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
const phoneDigits = ref('');
const isValid = ref(false);
</script>
<template>
<PhoneInput v-model="phoneDigits" country="US" theme="light" @validation-change="isValid = $event" />
<p v-if="isValid">✓ Valid phone number</p>
</template>
指令模式
对于自动格式化的自定义样式:
<script setup lang="ts">
import { ref } from 'vue';
import type { PMaskPhoneNumber } from '@desource/phone-mask-vue';
const country = ref('US');
const phone = ref('');
const digits = ref('');
const handleChange = (nextPhone: PMaskPhoneNumber) => {
phone.value = nextPhone.full;
digits.value = nextPhone.digits;
};
</script>
<template>
<div class="phone-wrapper">
<select v-model="country">
<option value="US">🇺🇸 +1</option>
<option value="GB">🇬🇧 +44</option>
<option value="DE">🇩🇪 +49</option>
</select>
<input
v-phone-mask="{
country,
onChange: handleChange
}"
placeholder="Phone number"
/>
</div>
<p>{{ digits }} digits entered</p>
</template>
可编译模式
对于自定义输入执行(如果甚至指令也不符合您的需要):
<script setup lang="ts">
import { ref } from 'vue';
import { usePhoneMask } from '@desource/phone-mask-vue';
const value = ref('');
const { inputRef, digits, full, fullFormatted, isComplete, setCountry } = usePhoneMask({
value,
onChange: (newValue) => (value.value = newValue),
country: 'US',
detect: true
});
</script>
<template>
<div>
<input ref="inputRef" type="tel" placeholder="Phone number" />
<p>Formatted: {{ fullFormatted }}</p>
<p>Valid: {{ isComplete ? 'Yes' : 'No' }}</p>
<button @click="setCountry('GB')">Use UK</button>
</div>
</template>
发送 Raw 数字到后端
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput, type PMaskPhoneNumber } from '@desource/phone-mask-vue';
const phoneDigits = ref('');
const onPhoneChange = async (phone: PMaskPhoneNumber) => {
await fetch('/api/profile/phone', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
phoneDigits: phone.digits, // unformatted value for backend
phoneFull: phone.full // optional full number with country code
})
});
};
</script>
<template>
<PhoneInput v-model="phoneDigits" country="US" @change="onPhoneChange" />
</template>
关于国家变化的动态遮罩更新
<script setup lang="ts">
import { ref } from 'vue';
import { usePhoneMask, type PCountryKey } from '@desource/phone-mask-vue';
const selectedCountry = ref<PCountryKey>('US');
const digits = ref('');
const { inputRef, fullFormatted, isComplete, setCountry } = usePhoneMask({
value: digits,
onChange: (value) => (digits.value = value),
country: selectedCountry
});
const onCountrySelect = (nextCountry: PCountryKey) => {
selectedCountry.value = nextCountry;
setCountry(nextCountry); // updates mask immediately
};
</script>
<template>
<select :value="selectedCountry" @change="onCountrySelect(($event.target as HTMLSelectElement).value as PCountryKey)">
<option value="US">US</option>
<option value="GB">GB</option>
<option value="DE">DE</option>
</select>
<input ref="inputRef" type="tel" />
<p>{{ fullFormatted }}</p>
<p>{{ isComplete ? 'complete' : 'incomplete' }}</p>
</template>
多租户:租户Id 默认国家+特定租户审定规则
<script setup lang="ts">
import { computed, ref } from 'vue';
import { usePhoneMask, type PCountryKey } from '@desource/phone-mask-vue';
type TenantPolicy = {
defaultCountry: PCountryKey;
prefixRule?: RegExp;
};
const props = defineProps<{ tenantId: string }>();
const TENANT_POLICIES: Record<string, TenantPolicy> = {
acme: { defaultCountry: 'US', prefixRule: /^(202|303)\d{7}$/ },
globex: { defaultCountry: 'GB', prefixRule: /^7\d{9}$/ }
};
const tenantPolicy = computed(() => TENANT_POLICIES[props.tenantId] ?? { defaultCountry: 'US' as const });
const digits = ref('');
const {
inputRef,
digits: maskDigits,
isComplete
} = usePhoneMask({
value: digits,
onChange: (value) => (digits.value = value),
country: computed(() => tenantPolicy.value.defaultCountry)
});
const isTenantValid = computed(() => {
if (!isComplete.value) return false;
const rule = tenantPolicy.value.prefixRule;
return rule ? rule.test(maskDigits.value) : true;
});
</script>
<template>
<input ref="inputRef" type="tel" />
<p>Default country: {{ tenantPolicy.defaultCountry }}</p>
<p>Tenant validation: {{ isTenantValid ? 'pass' : 'fail' }}</p>
</template>
++ 显示格式
打电话 formatPhone(value, country?, options?) 直接在模板中。 它返回文本, 不需要 CSS .
<script setup lang="ts">
import { formatPhone } from '@desource/phone-mask-vue/core';
defineProps<{ phone: string }>();
</script>
<template>
<span>{{ formatPhone(phone, 'US', { code: false, flag: true }) }}</span>
</template>
<!-- '+12345678888' displays as '🇺🇸 234 567 8888'. -->
奥米 country 以检测一个完整的国际编号 +; 传递给没有调用代码的号码, 或解析美国/CA等共享调用代码. 可选 PhoneFormatOptions 参数接受 code (默认) true和 flag (默认) false) (中文(简体) ).
该功能在SSF期间有效. 不支持的值保持不变; 空白, 仅空白, null,以及 undefined 值返回空文本。 见 格式规则 用于检测限度和实例.
* 构件 API
道具
interface PhoneInputProps {
// v-model binding
modelValue?: string;
// Optional id/name applied to the underlying <input> for forms/autofill
id?: string;
name?: string;
// Preselected country (ISO 3166-1 alpha-2)
country?: CountryKey;
// Auto-detect country from IP/locale
detect?: boolean; // Default: true
// Locale for country names
locale?: string; // Default: browser language
// Size variant
size?: 'compact' | 'normal' | 'large'; // Default: 'normal'
// Visual theme ("auto" | "light" | "dark")
theme?: 'auto' | 'light' | 'dark'; // Default: 'auto'
// Disabled state
disabled?: boolean; // Default: false
// Readonly state
readonly?: boolean; // Default: false
// Show copy button
showCopy?: boolean; // Default: true
// Show clear button
showClear?: boolean; // Default: false
// Show validation state (borders & outline)
withValidity?: boolean; // Default: true
// Custom search placeholder
searchPlaceholder?: string; // Default: 'Search country or code...'
// Custom no results text
noResultsText?: string; // Default: 'No countries found'
// Custom clear button label
clearButtonLabel?: string; // Default: 'Clear phone number'
// Dropdown menu custom CSS class
dropdownClass?: string;
// Disable default styles
disableDefaultStyles?: boolean; // Default: false
}
活动
interface PhoneInputEvents {
// v-model update
'update:modelValue': (value: string) => void;
// Value changed
// Provides an object with:
// - full: Full phone number with country code (e.g. +1234567890)
// - fullFormatted: Full phone number formatted according to country rules (e.g. +1 234-567-890)
// - digits: Only the digits of the phone number without country code (e.g. 234567890)
change: (value: PMaskPhoneNumber) => void;
// Country changed
'country-change': (country: PMaskFull) => void;
// Validation state changed
'validation-change': (isValid: boolean) => void;
// Input focused
focus: (event: FocusEvent) => void;
// Input blurred
blur: (event: FocusEvent) => void;
// Copy button clicked
copy: (value: string) => void;
// When input is cleared
clear: () => void;
}
被曝光的方法
interface PhoneInputExpose {
// Focus the phone input
focus: () => void;
// Blur the phone input
blur: () => void;
// Clear the phone input
clear: () => void;
// Select a country by its ISO 3166-1 alpha-2 code
selectCountry: (country: CountryKey) => void;
// Get the full phone number with country code (e.g. +1234567890)
getFullNumber: () => string;
// Get the full phone number formatted according to country rules (e.g. +1 234-567-890)
getFullFormattedNumber: () => string;
// Get only the digits of the phone number without country code (e.g. 234567890)
getDigits: () => string;
// Check if the current phone number is valid
isValid: () => boolean;
// Check if the current phone number is complete
isComplete: () => boolean;
}
槽数
-
actions-before——默认按钮前的自定义动作槽 -
flag国家列表和国家选择器中自定义国家旗帜的显示槽 -
copy-svg——自定义复制按钮 SVG 图标的槽 -
clear-svg——自定义清除按钮 SVG 图标的槽
使用参考文献
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
const phoneInputRef = ref<InstanceType<typeof PhoneInput>>();
const focusInput = () => {
phoneInputRef.value?.focus();
};
const clearInput = () => {
phoneInputRef.value?.clear();
};
</script>
<template>
<PhoneInput ref="phoneInputRef" v-model="phone" />
<button @click="focusInput">Focus</button>
<button @click="clearInput">Clear</button>
</template>
组件样式
CSS 自定义属性
通过 CSS 变量自定义颜色 :
.desource-phone-input,
.desource-phone-dropdown {
/* Colors */
--pi-bg: #ffffff;
--pi-fg: #111827;
--pi-muted: #6b7280;
--pi-border: #e5e7eb;
--pi-border-hover: #d1d5db;
--pi-border-focus: #3b82f6;
--pi-focus-ring: 3px solid rgb(59 130 246 / 0.15);
--pi-disabled-bg: #f9fafb;
--pi-disabled-fg: #9ca3af;
/* Sizes */
--pi-font-size: 16px;
--pi-height: 44px;
/* Spacing */
--pi-padding: 12px;
/* Border radius */
--pi-radius: 8px;
/* Shadows */
--pi-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--pi-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.05);
/* Validation */
--pi-warning: #f59e0b;
--pi-warning-light: #fbbf24;
--pi-success: #10b981;
--pi-focus-ring-warning: 3px solid rgb(245 158 11 / 0.15);
--pi-focus-ring-success: 3px solid rgb(16 185 129 / 0.15);
}
黑暗主题
<template>
<PhoneInput v-model="phone" theme="dark" />
</template>
或与CSS: (中文(简体) )
.desource-phone-input.theme-dark {
--pi-bg: #1f2937;
--pi-fg: #f9fafb;
--pi-border: #374151;
}
指令 API
基本用途
<template>
<input v-phone-mask="'US'" />
</template>
有选项
interface PMaskDirectiveOptions {
// Predefined country ISO code (e.g., 'US', 'DE', 'GB')
country?: string;
// Locale for country names (default: navigator.language)
locale?: string;
// Auto-detect country from IP/locale (default: false)
detect?: boolean;
// Value change callback
onChange?: (phone: PMaskPhoneNumber) => void;
// Country change callback
onCountryChange?: (country: PMaskFull) => void;
}
<template>
<input
v-phone-mask="{
country: 'US',
locale: 'en',
onChange: handleChange,
onCountryChange: handleCountryChange
}"
/>
</template>
反应国
<script setup lang="ts">
import type { PMaskPhoneNumber } from '@desource/phone-mask-vue';
const selectedCountry = ref('US');
const handleChange = (phone: PMaskPhoneNumber) => {
console.log('Phone:', phone.full, 'Digits:', phone.digits);
};
</script>
<template>
<select v-model="selectedCountry">
<option value="US">🇺🇸 United States</option>
<option value="GB">🇬🇧 United Kingdom</option>
</select>
<input
v-phone-mask="{
country: selectedCountry,
onChange: handleChange
}"
/>
</template>
++可编译的 API
usePhoneMask
关于自定义输入执行:
<script setup lang="ts">
import { ref } from 'vue';
import { usePhoneMask } from '@desource/phone-mask-vue';
const value = ref('');
const { inputRef, digits, full, fullFormatted, isComplete, setCountry, clear } = usePhoneMask({
value,
onChange: (newValue) => (value.value = newValue),
country: 'US',
detect: false
});
</script>
<template>
<div>
<input ref="inputRef" type="tel" />
<button @click="setCountry('GB')">Switch to UK</button>
<button @click="clear">Clear</button>
<p>Full: {{ fullFormatted || '—' }}</p>
<p>Valid: {{ isComplete ? 'Yes' : 'No' }}</p>
</div>
</template>
选项
说明: 堆肥需要受控行为. 两者
value财务报告和审定财务报表onChange需要选项.
interface UsePhoneMaskOptions {
// Controlled value (digits only, without country code) - REQUIRED
value: MaybeRefOrGetter<string>;
// Callback when the digits value changes - REQUIRED
onChange: (digits: string) => void;
// Predefined country ISO code (e.g., 'US', 'DE', 'GB')
country?: MaybeRefOrGetter<string | undefined>;
// Locale for country names (default: navigator.language)
locale?: MaybeRefOrGetter<string | undefined>;
// Auto-detect country from IP/locale (default: false)
detect?: MaybeRefOrGetter<boolean | undefined>;
// Callback when the phone changes (full, fullFormatted, digits)
onPhoneChange?: (phone: PhoneNumber) => void;
// Country change callback
onCountryChange?: (country: MaskFull) => void;
}
返回值
interface UsePhoneMaskReturn {
// Ref to attach to your input element
inputRef: ShallowRef<HTMLInputElement | null>;
// Raw digits without formatting (e.g., "1234567890")
digits: ComputedRef<string>;
// Phone formatter instance
formatter: ComputedRef<FormatterHelpers>;
// Full phone number with country code (e.g., "+11234567890")
full: ComputedRef<string>;
// Full phone number formatted (e.g., "+1 123-456-7890")
fullFormatted: ComputedRef<string>;
// Whether the phone number is complete
isComplete: ComputedRef<boolean>;
// Whether the input is empty
isEmpty: ComputedRef<boolean>;
// Whether to show validation warning
shouldShowWarn: ComputedRef<boolean>;
// Current country data
country: ComputedRef<MaskFull>;
// Change country programmatically
setCountry: (countryCode?: string | null) => boolean;
// Clear the input
clear: () => void;
}
实例
有验证
<script setup lang="ts">
import { ref, computed } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
const phone = ref('');
const isValid = ref(false);
const errorMessage = computed(() => {
if (!phone.value) return '';
return isValid.value ? '' : 'Please enter a valid phone number';
});
</script>
<template>
<div>
<PhoneInput v-model="phone" country="US" @validation-change="isValid = $event" />
<span v-if="errorMessage" class="error">
{{ errorMessage }}
</span>
</div>
</template>
自动检测国家
<script setup lang="ts">
import { ref } from 'vue';
import { PhoneInput, type PMaskFull } from '@desource/phone-mask-vue';
const phone = ref('');
const detectedCountry = ref('');
const handleCountryChange = (country: PMaskFull) => {
detectedCountry.value = country.name;
};
</script>
<template>
<PhoneInput v-model="phone" detect @country-change="handleCountryChange" />
<p v-if="detectedCountry">Detected: {{ detectedCountry }}</p>
</template>
带有窗体库
维维达德
<script setup lang="ts">
import { useField } from 'vee-validate';
import { PhoneInput } from '@desource/phone-mask-vue';
const { value, errorMessage } = useField('phone', (value) => {
if (!value) return 'Phone is required';
// Add custom validation
return true;
});
</script>
<template>
<PhoneInput v-model="value" />
<span v-if="errorMessage">{{ errorMessage }}</span>
</template>
多个输入
<script setup lang="ts">
import { reactive } from 'vue';
import { PhoneInput } from '@desource/phone-mask-vue';
const form = reactive({
mobile: '',
home: '',
work: ''
});
</script>
<template>
<div class="form">
<label>
Mobile
<PhoneInput v-model="form.mobile" country="US" />
</label>
<label>
Home
<PhoneInput v-model="form.home" country="US" />
</label>
<label>
Work
<PhoneInput v-model="form.work" country="US" />
</label>
</div>
</template>
QQ 浏览器支持
- 克罗梅/第90号+
- Firefox 88+ (英语)
- 萨法里14+
- iOS 萨法里14+
- Chrome 移动
包含什么
@desource/phone-mask-vue/
├── dist/
│ ├── index.mjs # Main ESM entry
│ ├── index.cjs # Main CommonJS entry
│ ├── core.mjs # Core kit utilities and mask data subpath
│ ├── core.cjs # Core kit utilities and mask data CJS subpath
│ ├── phone-mask-vue.css # Component styles
│ └── *.d.ts, *.d.cts # ESM and CommonJS declarations
├── README.md # This file
└── package.json # Package manifest
有关软件包
- @ desource/ phone- mask (英语) - 核心图书馆
- @ desource/ phone- mask- nuxt - 努克特模块
- @ desource/ phone- mask- angular (英语) - 角装订
- @ desource/ phone- mask- react - 反应约束
- @ desource/ phone- massk- svelte (英语) - 丝绒装订
许可证
麻省理工学院 2026 来源实验室
捐款
见 贡献指南 用于开发、测试和出版检查.