Skip to content

实现 电话号码解析 工具

分类:developer | 标签:电话、libphonenumber、号码 解析/格式化国际电话号码

2.1 功能说明

解析/格式化国际电话号码

核心能力

  • 号码解析 / 格式化
  • 号码
  • 默认地区
  • 有效
  • E.164
  • 国际格式
  • 本国格式
  • 国家/地区
  • 号码类型
  • 号段归属地查询

2.1.1 使用指南

基于 Google libphonenumber 解析和格式化国际电话号码,校验有效性并识别归属国家与号码类型。

  • 建议带上国家码(如 +86),或在「默认地区」选择归属地。
  • 支持 E.164 / 国际 / 本国三种格式输出。
  • 「号段归属地查询」内置国内主要号段对照表,可单号/批量查询运营商,并支持 CSV 导出。

2.2 代码实现

2.2.1 组件结构

本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-inputel-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。

2.2.2 核心逻辑概览

<script setup> 中定义的主要函数/方法:

  • parse()
  • buildSegments()
  • make()
  • cleanNumber()
  • findSeg()
  • querySegment()
  • runBatch()
  • copyBatch()
  • saveBlob()
  • exportBatch()

2.2.3 关键实现代码

以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。

vue
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import ToolPageShell from '@/components/ToolPageShell.vue'
import PNF from 'google-libphonenumber'

const innerTab = ref('parse')

/* ===== 原号码解析功能 ===== */
const num = ref('')
const region = ref('CN')
const error = ref('')
const result = ref(null)

const regions = ['CN', 'HK', 'TW', 'US', 'GB', 'JP', 'KR', 'SG', 'AU', 'DE', 'FR', 'RU', 'IN', 'BR', 'CA']
const typeMap = {
  FIXED_LINE: '固定电话',
  MOBILE: '手机',
  FIXED_LINE_OR_MOBILE: '固定电话或手机',
  TOLL_FREE: '免费电话',
  PREMIUM_RATE: '付费电话',
  SHARED_COST: '分摊付费',
  VOIP: 'VoIP',
  PERSONAL_NUMBER: '个人号码',
  PAGER: '寻呼机',
  UAN: 'UAN',
  UNKNOWN: '未知',
  EMERGENCY: '紧急号码'
}

function parse() {
  error.value = ''
  result.value = null
  const v = num.value.trim()
  if (!v) return
  const phoneUtil = PNF.PhoneNumberUtil.getInstance()
  let number
  try {
    number = phoneUtil.parse(v, region.value)
  } catch (e) {
    error.value = '解析失败:' + (e && e.message ? e.message : String(e))
    return
  }
  const valid = phoneUtil.isValidNumber(number)
  const code = phoneUtil.getRegionCodeForNumber(number)
  const t = phoneUtil.getNumberType(number)
  result.value = {
    valid,
    e164: valid ? phoneUtil.format(number, PNF.PhoneNumberFormat.E164) : '',
    intl: phoneUtil.format(number, PNF.PhoneNumberFormat.INTERNATIONAL),
    national: phoneUtil.format(number, PNF.PhoneNumberFormat.NATIONAL),
    country: code || '未知',
    type: typeMap[t] || String(t)
  }
}

parse()

/* ===== 号段归属地查询 ===== */
const MOBILE = ['134','135','136','137','138','139','147','148','150','151','152','157','158','159','172','178','182','183','184','187','188','195','197','198']
const UNICOM = ['130','131','132','145','146','155','156','166','167','171','175','176','185','186','196']
const TELECOM = ['133','149','153','173','174','177','180','181','189','190','191','193','199']
const MVNO = ['162','165','167','170']

const PROV_POOL = ['北京','上海','广东','江苏','浙江','四川','山东','河南','湖北','湖南','福建','河北','陕西','辽宁','安徽','江西','重庆','天津','广西','云南','山西','黑龙江','吉林','贵州','内蒙古','新疆','甘肃','海南']

function buildSegments() {
  const out = []
  const make = (list, op) => list.forEach((seg, i) => out.push({ seg, op, prov: PROV_POOL[i % PROV_POOL.length] }))
  make(MOBILE, '中国移动')
  make(UNICOM, '中国联通')
  make(TELECOM, '中国电信')
  make(MVNO, '虚拟运营商')
  return out
}
const segments = buildSegments()

const segInput = ref('')
const segResult = ref(null)
const batchInput = ref('')
const batchRows = ref([])

function cleanNumber(s) { return (s || '').replace(/[^\d]/g, '') }

function findSeg(clean) {
  if (!clean || clean.length < 3) return null
  const seg3 = clean.slice(0, 3)
  return segments.find(s => s.seg === seg3) || null
}

function querySegment() {
  segResult.value = null
  const clean = cleanNumber(segInput.value)
  if (!clean) return
  const hit = findSeg(clean)
  if (!hit) return
  segResult.value = { clean, seg: hit.seg, op: hit.op, prov: hit.prov }
}

function runBatch() {
  const lines = batchInput.value.split('\n').map(l => l.trim()).filter(Boolean)
  batchRows.value = lines.map(line => {
    const clean = cleanNumber(line)
    const hit = clean ? findSeg(clean) : null
    return {
      raw: line,
      clean: clean || line,
      seg: hit ? hit.seg : '—',
      op: hit ? hit.op : '未知',
      prov: hit ? hit.prov : '—'
    }
  })
}

function copyBatch() {
  if (!batchRows.value.length) return
  const text = ['号码,号段,运营商,示例归属地', ...batchRows.value.map(r => `${r.clean},${r.seg},${r.op},${r.prov}`)].join('\n')
  if (navigator.clipboard?.writeText) navigator.clipboard.writeText(text)
  else ElMessage.warning('当前环境不支持自动复制')
}
function saveBlob(blob, name) {
  const a = document.createElement('a')
  a.href = URL.createObjectURL(blob)
  a.download = name
  document.body.appendChild(a)
  a.click()
  a.remove()
  setTimeout(() => URL.revokeObjectURL(a.href), 1000)
}
function exportBatch() {
  if (!batchRows.value.length) return
  const text = ['号码,号段,运营商,示例归属地', ...batchRows.value.map(r => `${r.clean},${r.seg},${r.op},${r.prov}`)].join('\n')
  // 加 BOM 以兼容 Excel 中文
  saveBlob(new Blob(['' + text], { type: 'text/csv;charset=utf-8' }), 'phone-segments.csv')
}

2.3 效果截图

电话号码解析 效果截图

工具访问地址:https://www.i91tools.com/tools/phone-parse