Skip to content

实现 银行卡车牌邮编查询 工具

分类:developer | 标签:银行卡、车牌、邮编 银行卡BIN车牌归属地邮编区号查询

2.1 功能说明

银行卡BIN车牌归属地邮编区号查询

2.1.1 使用指南

功能说明

三个常用查表工具合在一起:银行卡 BIN 识别发卡行、车牌号识别归属地、城市邮编与电话区号互查。 所有数据都内置在页面里,输入内容不会离开你的浏览器,不发起任何网络请求。

银行卡 BIN 查询

BIN(Bank Identification Number,发卡行识别码)是银行卡号的前 6–8 位, 由卡组织统一分配,用来标识发卡机构。输入卡号前 6 位以上即可查出发卡行、卡种和卡组织。 工具同时会做Luhn 校验:如果你输入了完整卡号,会验证校验位是否合法, 这是识别输入错误的常用方法(银联、VISA、MasterCard 均遵循此算法)。

内置 {{ BANK_BINS.length }} 条 BIN 记录,覆盖六大国有银行、主要股份制银行和部分城商行。 卡种分为借记卡(储蓄卡,先存后用)、信用卡(贷记卡,先用后还)和准贷记卡(需存备用金、可小额透支,现已少见)。

车牌归属地查询

中国大陆民用车牌格式为「省份简称 + 发牌机关代号 + 序号」,例如「粤B·12345」表示广东省深圳市。 输入完整车牌或仅输入省份简称都可以查询。工具收录全部 31 个省级行政区简称, 并对 {{ Object.keys(PLATE_CITIES).length }} 个省份提供了地市字母代号的细分对照。 新能源车牌为 8 位,规则相同,只是序号多一位。

邮编与区号查询

支持城市名、邮编、区号三种输入方式互查,内置 {{ CITIES.length }} 个主要城市。 中国邮政编码为 6 位数字:前两位表示省级区域,第三位表示邮区,第四位表示县市,后两位表示投递局。 长途电话区号为 3–4 位,直辖市和部分特大城市为 3 位(如北京 010、上海 021)。

数据覆盖度与限制

BIN 数据:银行 BIN 总量有数千条且会随新卡种发行而变化,本工具收录的是最常见的 {{ BANK_BINS.length }} 条。查不到不代表卡号有问题,只说明该 BIN 不在收录范围内。 另外同一家银行不同分行、不同卡种的 BIN 可能不同,结果仅供参考, 请勿用于任何风控、支付验证等生产用途,正式场景应调用银联或发卡行的官方接口。

车牌数据:地市字母代号只覆盖了主要省份,未收录省份只显示到省级。 个别城市因扩容启用了额外字母(如粤A、粤S 之外的新增号段),以及军车、警车、使领馆车牌等特殊号牌, 规则与民用牌不同,表中仅作简要说明。

邮编数据:只收录到地级市层面,使用的是各市的通用邮编(多为「XX0000」形式), 实际投递需精确到区县甚至街道的邮编。部分城市多个区号并存或经历过区号合并,以当地实际使用为准。

以上数据整理自公开资料,可能存在滞后,重要用途请以官方渠道核实。

2.2 代码实现

2.2.1 组件结构

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

本工具目录 RegionLookup/ 下除 index.vue 外,还包含以下源文件:

  • data.js(286 行)

2.2.2 核心逻辑概览

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

  • luhnValid()
  • copyRow()
  • downloadZipCsv()
  • copyText()
  • download()

2.2.3 关键实现代码

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

vue
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import ToolPageShell from '@/components/ToolPageShell.vue'
import { BANK_BINS, CITIES, PLATE_CITIES, PLATE_PROVINCES, PLATE_SPECIAL } from './data.js'

const activeTab = ref('bin')
const cardInput = ref('')
const binTableQuery = ref('')
const plateInput = ref('')
const zipInput = ref('')

// ---------- 银行卡 BIN ----------
const cardDigits = computed(() => cardInput.value.replace(/\D/g, ''))

// 取最长匹配的 BIN 前缀
const binResult = computed(() => {
  const d = cardDigits.value
  if (d.length < 6) return null
  let best = null
  for (const item of BANK_BINS) {
    if (d.startsWith(item.bin) && (!best || item.bin.length > best.bin.length)) best = item
  }
  return best
})

function luhnValid (digits) {
  let sum = 0
  let double = false
  for (let i = digits.length - 1; i >= 0; i--) {
    let n = Number(digits.charAt(i))
    if (double) {
      n *= 2
      if (n > 9) n -= 9
    }
    sum += n
    double = !double
  }
  return sum % 10 === 0
}

const luhnState = computed(() => {
  const d = cardDigits.value
  if (d.length < 12) return { type: 'info', text: '卡号位数不足,暂不校验' }
  return luhnValid(d)
      ? { type: 'success', text: '校验通过' }
      : { type: 'danger', text: '校验不通过,请检查卡号是否输错' }
})

const maskedCard = computed(() => {
  const d = cardDigits.value
  if (d.length < 10) return d
  return d.slice(0, 6) + '*'.repeat(d.length - 10) + d.slice(-4)
})

const binText = computed(() => {
  const r = binResult.value
  if (!r) return ''
  return [
    '【银行卡 BIN 查询结果】',
    '发卡行:' + r.bank,
    '卡种:' + r.type,
    '卡组织:' + r.brand,
    'BIN 前缀:' + r.bin,
    '常见卡号位数:' + r.len,
    '输入位数:' + cardDigits.value.length,
    'Luhn 校验:' + luhnState.value.text,
    '脱敏卡号:' + maskedCard.value,
    '',
    '提示:结果基于内置静态 BIN 表,仅供参考,请勿用于支付风控等生产用途。'
  ].join('\n')
})

const binRows = computed(() => {
  const q = binTableQuery.value.trim()
  if (!q) return BANK_BINS
  return BANK_BINS.filter(b =>
      b.bank.includes(q) || b.bin.includes(q) || b.type.includes(q) || b.brand.toLowerCase().includes(q.toLowerCase()))
})

// ---------- 车牌 ----------
const plateResult = computed(() => {
  const raw = plateInput.value.trim().replace(/[·.\s-]/g, '').toUpperCase()
  if (!raw) return null
  const chars = Array.from(raw)
  const abbr = chars[0]
  const prov = PLATE_PROVINCES[abbr]
  if (!prov) return null

  const letter = chars[1] && /[A-Z]/.test(chars[1]) ? chars[1] : ''
  const cityMap = PLATE_CITIES[abbr]
  const city = letter && cityMap ? cityMap[letter] || '' : ''
  const body = chars.slice(2).join('')

  let plateType = '未填写序号'
  if (body.length >= 5) {
    plateType = body.length >= 6 ? '新能源汽车号牌(8 位)' : '普通汽车号牌(7 位)'
    if (raw.endsWith('学')) plateType = '教练车号牌'
    else if (raw.endsWith('警')) plateType = '警用车号牌'
    else if (raw.endsWith('港') || raw.endsWith('澳')) plateType = '粤港澳出入境车号牌'
  } else if (body.length > 0) {
    plateType = '序号不完整'
  }

  return {
    abbr,
    province: prov.province,
    region: prov.region,
    capital: prov.capital,
    letter,
    city,
    plateType
  }
})

const plateText = computed(() => {
  const r = plateResult.value
  if (!r) return ''
  return [
    '【车牌归属地查询结果】',
    '车牌:' + plateInput.value.trim(),
    '省份简称:' + r.abbr,
    '省级行政区:' + r.province,
    '省会 / 首府:' + r.capital,
    '所属地区:' + r.region,
    '发牌机关代号:' + (r.letter || '未输入') + (r.city ? '(' + r.city + ')' : ''),
    '车牌类型:' + r.plateType
  ].join('\n')
})

// ---------- 邮编区号 ----------
const zipRows = computed(() => {
  const q = zipInput.value.trim()
  if (!q) return CITIES
  return CITIES.filter(c =>
      c.city.includes(q) || c.province.includes(q) || c.zip.includes(q) || c.area.includes(q))
})

function copyRow (row) {
  copyText(row.city + '(' + row.province + ')邮编 ' + row.zip + ',电话区号 ' + row.area)
}

function downloadZipCsv () {
  const head = ['城市', '省份', '邮政编码', '电话区号']
  const rows = zipRows.value.map(c => [c.city, c.province, c.zip, c.area])
  const csv = [head, ...rows]
      .map(r => r.map(v => '"' + String(v).replace(/"/g, '""') + '"').join(','))
      .join('\n')
  download('\ufeff' + csv, '城市邮编区号表.csv', 'text/csv;charset=utf-8')
}

// ---------- 通用 ----------
async function copyText (text) {
  if (!text) return
  try {
    await navigator.clipboard.writeText(text)
    ElMessage.success('已复制')
  } catch {
    ElMessage.warning('复制失败,请手动选择复制')
  }
}

function download (content, filename, type = 'text/plain;charset=utf-8') {
  if (!content) return
  const blob = new Blob([content], { type })
  const a = document.createElement('a')
  a.href = URL.createObjectURL(blob)
  a.download = filename
  a.click()
  URL.revokeObjectURL(a.href)
}

2.3 效果截图

银行卡车牌邮编查询 效果截图

工具访问地址:https://www.i91tools.com/tools/region-lookup