html读取身份证【成都鱼住未来身份证】:CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构

代码

复制代码
<!DOCTYPE html>

<html lang='zh-CN'>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Websocket/Web API 使用 Demo</title>
  <style>
  /* ==================
          布局
 ==================== */

/*  -- flex弹性布局 -- */

.flex {
	display: flex;
}

.basis-xs {
	flex-basis: 20%;
}

.basis-sm {
	flex-basis: 40%;
}

.basis-df {
	flex-basis: 50%;
}

.basis-lg {
	flex-basis: 60%;
}

.basis-xl {
	flex-basis: 80%;
}

.flex-sub {
	flex: 1;
}

.flex-twice {
	flex: 2;
}

.flex-treble {
	flex: 3;
}

.flex-direction {
	flex-direction: column;
}

.flex-wrap {
	flex-wrap: wrap;
}

.align-start {
	align-items: flex-start;
}

.align-end {
	align-items: flex-end;
}

.align-center {
	align-items: center;
}

.align-stretch {
	align-items: stretch;
}

.self-start {
	align-self: flex-start;
}

.self-center {
	align-self: flex-center;
}

.self-end {
	align-self: flex-end;
}

.self-stretch {
	align-self: stretch;
}

.align-stretch {
	align-items: stretch;
}

.justify-start {
	justify-content: flex-start;
}

.justify-end {
	justify-content: flex-end;
}

.justify-center {
	justify-content: center;
}

.justify-between {
	justify-content: space-between;
}

.justify-around {
	justify-content: space-around;
}
</style>
</head>

<body>
  <div class="container">
    <div class="head flex justify-start align-center" style="margin-bottom: 20px;">
      
      <h2>未来之窗-鱼住未来身份识别服务使用 Demo</h2>
    </div>

    <div class="content flex justify-start">
      <!-- 功能使用  -->
      <div class="flex-sub" style="border-right: 1px solid #eee;">
        <h3 class="block-title">功能使用</h3>
        <div class="flex justify-start align-center" style="color: #5c6063;">
          <p>填写连接地址:</p>
          <input id="connect-address" value="127.0.0.1:30004"></input>
        </div>
        <div class="flex flex-direction align-center" style="height: 535px;">
          <div class="func-card websocket flex-sub">
            <p class="title">Websocket 使用 Demo</p>
            <p class="sub-title">( 点击按钮读取相应信息;支持被动接收和主动请求两种方式。 )</p>
            <div class="btn-area">
              <div class="flex">
                <button id="on-websocket" class="flex-sub">连接到webSocket</button>
                <button id="off-websocket" class="flex-sub">断开webSocket</button>
              </div>
              <div class="flex flex-wrap">
                <button id="websocket-ID" class="flex-sub">身份证</button>
                <button id="websocket-ID-sn" class="flex-sub">身份证SN</button>
                <button id="websocket-A-sn" class="flex-sub">A卡SN</button>
                <button id="websocket-device-No" class="flex-sub">设备唯一号</button>
              </div>
            </div>
          </div>
          <div class="func-card webapi flex-sub">
            <p class="title">Web API 使用 Demo</p>
            <p class="sub-title">( 点击按钮读取相应信息;支持主动请求方式。 )</p>
            <div class="btn-area">
              <div class="flex flex-wrap">
                <button id="http-ID" class="flex-sub">身份证</button>
                <button id="http-ID-sn" class="flex-sub">身份证SN</button>
              </div>
              <div class="flex flex-wrap">
                <button id="http-A-sn" class="flex-sub">A卡SN</button>
                <button id="http-device-No" class="flex-sub">设备唯一号</button>
              </div>
            </div>
          </div>
        </div>
      </div>
      <!-- 读取过程  -->
      <div class="flex-sub" style="border-right: 1px solid #eee;">
        <h3 class="block-title">读取过程</h3>
        <textarea name="process" id="process-content" readonly="readonly"></textarea>
      </div>
      <!-- 读取结果  -->
      <div class="result-container flex-sub">
        <h3 class="block-title">读取结果</h3>
        <div class="flex justify-start align-center" style="padding-left: 25px;color: #5c6063;">
          <p>SN 号:</p>
          <p id="SN-content"></p>
        </div>
        <div id="card-front" class="ID-card card-front">
          <img class="image" src="" alt="证件照">
          <p class="name"></p>
          <p class="enName"></p>
          <p class="sex"></p>
          <p class="nation"></p>
          <p class="year"></p>
          <p class="month"></p>
          <p class="date"></p>
          <p class="address"></p>
          <p class="number"></p>
        </div>
        <div id="card-back" class="ID-card card-back">
          <p class="department"></p>
          <p class="expiry"></p>
          <p class="pass-number"></p>
        </div>

        <div class="flex justify-center">
          <button id="clean-process">清空</button>
        </div>
      </div>
    </div>
  </div>

  <div class="copyright">
    <span class="copyright-item" id="copyright-text">
      <script type="text/javascript">
        document.getElementById("copyright-text").innerText = "成都鱼住未来科技有限公司版权所有 ©2019 - "+ new Date().getFullYear()
      </script>
    </span>
  </div>


  
</body>
</html>
<script src="/o2o/tpl/Merchant/static/js/jquery.min.js">  </script>

<script>

  var btnOnWS  = $('#on-websocket')
  var btnOffWS = $('#off-websocket')

  var btnWS_ID       = $('#websocket-ID')
  var btnWS_IDSN     = $('#websocket-ID-sn')
  var btnWS_ASN      = $('#websocket-A-sn')
  var btnWS_DeviceNo = $('#websocket-device-No')

  var btnHttp_ID       = $('#http-ID')
  var btnHttp_IDSN     = $('#http-ID-sn')
  var btnHttp_ASN      = $('#http-A-sn')
  var btnHttp_DeviceNo = $('#http-device-No')

  var connectAddress  = $('#connect-address')
  var processContent  = $('#process-content')
  var btnCleanProcess = $('#clean-process')

  var cardFront  = $('#card-front')
  var cardBack   = $('#card-back')

  var SNContent  = $('#SN-content')
  var image      = $('#image')
  var name       = $('#name')
  var sex        = $('#sex')
  var nation     = $('#nation')
  var year       = $('#year')
  var month      = $('#month')
  var date       = $('#date')
  var address    = $('#address')
  var number     = $('#number')
  var department = $('#department')
  var expiry     = $('#expiry')

</script>
<script>
  let ws = null




  function hex2a(hex) {
    let str_list = ''
    for (let i = 0; i < hex.length && hex.substr(i, 2) !== '00'; i += 2) {
      const a = hex.charCodeAt(i)
      const b = hex.charCodeAt(i + 1)
      const c = b * 256 + a
      str_list += String.fromCharCode(c)
    }

    return str_list.toString()
  }

  function setDocumentInfo(szparam)
  {
    if (szparam.CardType == 74)
    {
      // 切换背景图片   83是台湾
      cardFront.removeClass()
      cardBack.removeClass()
      cardFront.addClass('GAT-card')
      cardFront.addClass('card-hongkong-macao-taiwan-front')
      cardBack.addClass('GAT-card')
      let no = hex2a(window.atob(szparam.CardInfo.No))
      if (no && no.startsWith('83')){
        cardBack.addClass('card-taiwan-back')
      }else{
        cardBack.addClass('card-hongkong-macao-back')
      }
      strLog = '读取 港澳台居民居住证 成功\r\n';
      strLog += 'SN:' + szparam.CardInfo.SN + '\r\n';
      strLog += '中文名:' + hex2a(window.atob(szparam.CardInfo.Name)) + '\r\n';
      strLog += '证件号码:' + no + '\r\n';
      strLog += '性别:' + hex2a(window.atob(szparam.CardInfo.Sex)) + '\r\n';
      strLog += '出生日期:' + hex2a(window.atob(szparam.CardInfo.Birthday)) + '\r\n';
      strLog += '民族:' + hex2a(window.atob(szparam.CardInfo.Nation)) + '\r\n';
      strLog += '地址:' + hex2a(window.atob(szparam.CardInfo.Address)) + '\r\n';
      strLog += '签发机关:' + hex2a(window.atob(szparam.CardInfo.SignedDepartment)) + '\r\n';
      strLog += '开始日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin)) + '\r\n';
      strLog += '结束日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)) + '\r\n\r\n';
      strLog += '通行证号码:' + hex2a(window.atob(szparam.CardInfo.OtherNO)) + '\r\n';
      strLog += '签发次数:' + hex2a(window.atob(szparam.CardInfo.SignNum)) + '\r\n';
      processContent.text(strLog)

      // 港澳台通行证号码
      console.log(szparam.CardInfo)
      cardFront.find('.name').text(hex2a(window.atob(szparam.CardInfo.Name)))
      cardFront.find('.sex').text(hex2a(window.atob(szparam.CardInfo.Sex)) ==='1'? '男':'女')

      const Birthday = hex2a(window.atob(szparam.CardInfo.Birthday))
      const birthArr = parseDateString(Birthday , ".", true).split(".")
      cardFront.find('.year').text(birthArr[0])
      cardFront.find('.month').text(birthArr[1])
      cardFront.find('.date').text(birthArr[2])

      cardFront.find('.address').text(hex2a(window.atob(szparam.CardInfo.Address)))
      cardFront.find('.number').text(hex2a(window.atob(szparam.CardInfo.No)))
      cardBack.find('.department').text(hex2a(window.atob(szparam.CardInfo.SignedDepartment)))
      const ValidityPeriodBegin = hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin))
      const ValidityPeriodEnd = hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)).trim()
      const expiryBegin = parseDateString(ValidityPeriodBegin, '.')
      const expiryEnd = ValidityPeriodEnd !== '长期' ? parseDateString(ValidityPeriodEnd, '.') : ValidityPeriodEnd
      cardBack.find('.expiry').text( expiryBegin + '-' + expiryEnd)
      cardBack.find('.pass-number').text(  hex2a(window.atob(szparam.CardInfo.OtherNO)))
    }
    else if (szparam.CardType == 73)
    {
      // 切换背景图片   83是台湾
      cardFront.removeClass()
      cardBack.removeClass()
      cardFront.addClass('WGR-card-1')
      cardFront.addClass('card-old-foreigner-front')
      cardBack.addClass('WGR-card-1')
      cardBack.addClass('card-old-foreigner-back')

      strLog = '读取 外国人永久居留身份证(旧版) 成功\r\n';
      strLog += 'SN:' + szparam.CardInfo.SN + '\r\n';
      strLog += '中文名:' + hex2a(window.atob(szparam.CardInfo.Name)) + '\r\n';
      strLog += '英文名:' + hex2a(window.atob(szparam.CardInfo.EnName)) + '\r\n';
      strLog += '证件号码:' + hex2a(window.atob(szparam.CardInfo.No)) + '\r\n';
      strLog += '性别:' + hex2a(window.atob(szparam.CardInfo.Sex)) + '\r\n';
      strLog += '出生日期:' + hex2a(window.atob(szparam.CardInfo.Birthday)) + '\r\n';
      strLog += '国籍:' + hex2a(window.atob(szparam.CardInfo.Country)) + '\r\n';
      strLog += '签发机关:中华人民共和国移民管理局\r\n';
      strLog += '开始日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin)) + '\r\n';
      strLog += '结束日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)) + '\r\n\r\n';
      strLog += '版本号:' + hex2a(window.atob(szparam.CardInfo.Version)) + '\r\n';
      processContent.text(strLog)

      let name = hex2a(window.atob(szparam.CardInfo.Name))
      let enName = hex2a(window.atob(szparam.CardInfo.EnName))
      let nameText = enName + (name.trim()? ' / '+ name : '')
      cardFront.find('.name').text(nameText)
      cardFront.find('.sex').text(hex2a(window.atob(szparam.CardInfo.Sex)) === '1'? '男': '女')
      const Birthday = hex2a(window.atob(szparam.CardInfo.Birthday))
      const birthArr = parseDateString(Birthday , ".", true).split(".")
      cardFront.find('.year').text(birthArr.join('-')) //出生年月
      cardFront.find('.month').text(hex2a(window.atob(szparam.CardInfo.Country)))//国籍
      const ValidityPeriodBegin = hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin))
      const ValidityPeriodEnd = hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)).trim()
      const expiryBegin = parseDateString(ValidityPeriodBegin, '.')
      const expiryEnd = ValidityPeriodEnd !== '长期' ? parseDateString(ValidityPeriodEnd, '.') : ValidityPeriodEnd
      cardFront.find('.date').text(expiryBegin + '-' + expiryEnd)
      cardFront.find('.address').text('中华人民共和国移民管理局') //
      cardFront.find('.number').text(hex2a(window.atob(szparam.CardInfo.No)))
    }
    else if (szparam.CardType == 89)
    {
      // 切换背景图片   83是台湾
      cardFront.removeClass()
      cardBack.removeClass()
      cardFront.addClass('WGR-card')
      cardFront.addClass('card-new-foreigner-front')
      cardBack.addClass('WGR-card')
      cardBack.addClass('card-new-foreigner-back')

      strLog = '读取 外国人永久居留身份证(新版) 成功\r\n';
      strLog += 'SN:' + szparam.CardInfo.SN + '\r\n';
      strLog += '中文名:' + hex2a(window.atob(szparam.CardInfo.Name)) + '\r\n';
      strLog += '英文名:' + hex2a(window.atob(szparam.CardInfo.EnName)) + '\r\n';
      strLog += '证件号码:' + hex2a(window.atob(szparam.CardInfo.No)) + '\r\n';
      strLog += '性别:' + hex2a(window.atob(szparam.CardInfo.Sex)) + '\r\n';
      strLog += '出生日期:' + hex2a(window.atob(szparam.CardInfo.Birthday)) + '\r\n';
      strLog += '国籍:' + hex2a(window.atob(szparam.CardInfo.Country)) + '\r\n';
      strLog += '签发机关:中华人民共和国移民管理局\r\n';
      strLog += '开始日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin)) + '\r\n';
      strLog += '结束日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)) + '\r\n\r\n';
      strLog += '通行证号码:' + hex2a(window.atob(szparam.CardInfo.OtherNO)) + '\r\n';
      strLog += '签发次数:' + hex2a(window.atob(szparam.CardInfo.SignNum)) + '\r\n';
      processContent.text(strLog)
      let name = hex2a(window.atob(szparam.CardInfo.Name))
      let enName = hex2a(window.atob(szparam.CardInfo.EnName))
      cardFront.find('.name').text(name)
      cardFront.find('.enName').text(enName)
      cardFront.find('.sex').text(hex2a(window.atob(szparam.CardInfo.Sex)) === '1'? '男': '女')
      const Birthday = hex2a(window.atob(szparam.CardInfo.Birthday))
      const birthArr = parseDateString(Birthday , ".", true).split(".")
      cardFront.find('.year').text(birthArr.join('-')) //出生年月
      cardFront.find('.month').text(hex2a(window.atob(szparam.CardInfo.Country)))//国籍
      const ValidityPeriodBegin = hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin))
      const ValidityPeriodEnd = hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)).trim()
      const expiryBegin = parseDateString(ValidityPeriodBegin, '.')
      const expiryEnd = ValidityPeriodEnd !== '长期' ? parseDateString(ValidityPeriodEnd, '.') : ValidityPeriodEnd
      cardFront.find('.date').text(expiryBegin + '-' + expiryEnd)
      cardFront.find('.number').text(hex2a(window.atob(szparam.CardInfo.No)))
    }
    else
    {
      cardFront.removeClass()
      cardBack.removeClass()
      cardFront.addClass('ID-card')
      cardFront.addClass('card-front')
      cardBack.addClass('ID-card')
      cardBack.addClass('card-back')
      strLog = '读取 身份证 成功\r\n';
      strLog += 'SN:' + szparam.CardInfo.SN + '\r\n';
      strLog += '中文名:' + hex2a(window.atob(szparam.CardInfo.Name)) + '\r\n';
      strLog += '证件号码:' + hex2a(window.atob(szparam.CardInfo.No)) + '\r\n';
      strLog += '性别:' + hex2a(window.atob(szparam.CardInfo.Sex)) + '\r\n';
      strLog += '出生日期:' + hex2a(window.atob(szparam.CardInfo.Birthday)) + '\r\n';
      strLog += '民族:' + hex2a(window.atob(szparam.CardInfo.Nation)) + '\r\n';
      strLog += '地址:' + hex2a(window.atob(szparam.CardInfo.Address)) + '\r\n';
      strLog += '签发机关:' + hex2a(window.atob(szparam.CardInfo.SignedDepartment)) + '\r\n';
      strLog += '开始日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin)) + '\r\n';
      strLog += '结束日期:' + hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)) + '\r\n\r\n';
      strLog += '通行证号码:' + hex2a(window.atob(szparam.CardInfo.OtherNO)) + '\r\n';
      strLog += '签发次数:' + hex2a(window.atob(szparam.CardInfo.SignNum)) + '\r\n';
      processContent.text(strLog)
    //  内容填充
      cardFront.find('.name').text(hex2a(window.atob(szparam.CardInfo.Name)))
      cardFront.find('.sex').text(hex2a(window.atob(szparam.CardInfo.Sex)))
      cardFront.find('.nation').text(hex2a(window.atob(szparam.CardInfo.Nation)))

      const Birthday = hex2a(window.atob(szparam.CardInfo.Birthday))
      const birthArr = parseDateString(Birthday , ".", true).split(".")
      cardFront.find('.year').text(birthArr[0])
      cardFront.find('.month').text(birthArr[1])
      cardFront.find('.date').text(birthArr[2])

      cardFront.find('.address').text(hex2a(window.atob(szparam.CardInfo.Address)))
      cardFront.find('.number').text(hex2a(window.atob(szparam.CardInfo.No)))
      cardBack.find('.department').text(hex2a(window.atob(szparam.CardInfo.SignedDepartment)))
      const ValidityPeriodBegin = hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin))
      const ValidityPeriodEnd = hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)).trim()
      const expiryBegin = parseDateString(ValidityPeriodBegin, '.')
      const expiryEnd = ValidityPeriodEnd !== '长期' ? parseDateString(ValidityPeriodEnd, '.') : ValidityPeriodEnd
      cardBack.find('.expiry').text( expiryBegin + '-' + expiryEnd)
    }
    SNContent.text(szparam.CardInfo.SN)
    cardFront.find('.image').attr('src','data:image/jpg;base64,' + szparam.BmpInfo)
    // if (szparam.CardInfo.Name){
    //   cardFront.find('.name').text(hex2a(window.atob(szparam.CardInfo.Name)))
    // }
    // if (szparam.CardInfo.Sex){
    //   cardFront.find('.sex').text(hex2a(window.atob(szparam.CardInfo.Sex)))
    // }
    // if (szparam.CardInfo.Nation){
    //   cardFront.find('.nation').text(hex2a(window.atob(szparam.CardInfo.Nation)))
    // }
    //
    // const Birthday = hex2a(window.atob(szparam.CardInfo.Birthday))
    // const birthArr = parseDateString(Birthday , ".", true).split(".")
    // cardFront.find('.year').text(birthArr[0])
    // cardFront.find('.month').text(birthArr[1])
    // cardFront.find('.date').text(birthArr[2])
    //
    // if (szparam.CardInfo.Address){
    //   cardFront.find('.address').text(hex2a(window.atob(szparam.CardInfo.Address)))
    // }
    //
    // if (szparam.CardInfo.No){
    //   cardFront.find('.number').text(hex2a(window.atob(szparam.CardInfo.No)))
    // }
    //
    // if (szparam.CardInfo.SignedDepartment){
    //   cardBack.find('.department').text(hex2a(window.atob(szparam.CardInfo.SignedDepartment)))
    // }
    //
    // const ValidityPeriodBegin = hex2a(window.atob(szparam.CardInfo.ValidityPeriodBegin))
    // const ValidityPeriodEnd = hex2a(window.atob(szparam.CardInfo.ValidityPeriodEnd)).trim()
    // const expiryBegin = parseDateString(ValidityPeriodBegin, '.')
    // const expiryEnd = ValidityPeriodEnd !== '长期' ? parseDateString(ValidityPeriodEnd, '.') : ValidityPeriodEnd
    // cardBack.find('.expiry').text( expiryBegin + '-' + expiryEnd)

  }

  function conWS() {
    const webUrl = 'ws://' + connectAddress.val() + '/ws'
    ws = new WebSocket(webUrl)
    ws.onopen = function (evt) {
      let szhelp = 'websocket连接成功,url[' + webUrl + '],读卡器上放置身份证后websocket会自动接收身份证数据,如需手动操作请调用WS_ReadInfo()函数\r\n\r\n'
      szhelp += '支持被动接收和主动请求两种方式\r\n'
      szhelp += '被动接收:当读卡器刷卡成功后会推送身份证信息到websocket,websocket直接显示即可\r\n'
      szhelp += '主动请求:支持网页端主动向服务器请求对应的消息。可查看<WS_ReadInfo><WS_GetASN><WS_GetBCardNo>这三个接口'

      processContent.text(szhelp)
    }
    ws.onclose = function (evt) {
      processContent.text('websocket已断开')
    }
    ws.onmessage = function (messageEvent) {
      const jsonobject = JSON.parse(messageEvent.data)
      if (jsonobject.Ret == 0) {
        if (jsonobject.Cmd == 10001) {
          cleanMsg()
          const szparam = JSON.parse(window.atob(jsonobject.UserParam))
          setDocumentInfo(szparam);
        } else if (jsonobject.Cmd == 30401) {
          const szparam = JSON.parse(window.atob(jsonobject.UserParam))
          processContent.text('websocket 协议 读取A卡SN成功:' + szparam.SN)
        } else if (jsonobject.Cmd == 20401) {
          const szparam = JSON.parse(window.atob(jsonobject.UserParam))
          processContent.text('websocket 协议 读取身份证卡片SN成功:' + szparam.SN)
        } else if (jsonobject.Cmd == 20511) {
          const szparam = JSON.parse(window.atob(jsonobject.UserParam))
          processContent.text('websocket 协议 读卡器唯一号:' + szparam.SN)
        }else if (jsonobject.Cmd == 1000) {
            szparam = JSON.parse(window.atob(jsonobject.UserParam));
            if (szparam.State == 0)
            {
          	processContent.text('读卡器已被拔出')
            }
            else processContent.text('读卡器已插入')
          }
      } else {
        processContent.text('websocket 协议调用失败,原因:' + jsonobject.ErrInfo)
      }
    }
  }

  function disconWS() {
    if (ws) {
      ws.close()
      processContent.text('websocket已断开')
    }
  }

  function cleanMsg() {
    processContent.text('')
    SNContent.text('')
    cardFront.find('.name').text('')
    cardFront.find('.enName').text('')
    cardFront.find('.sex').text('')
    cardFront.find('.nation').text('')
    cardFront.find('.year').text('')
    cardFront.find('.month').text('')
    cardFront.find('.date').text('')
    cardFront.find('.address').text('')
    cardFront.find('.number').text('')
    cardBack.find('.department').text('')
    cardBack.find('.expiry').text('')
    cardBack.find('.pass-number').text('')
    cardFront.find('.image').attr('src', '')
  }

  function WS_GetASN() {
    const szJson = '{"Cmd":30400,"Head":"YZWL","IPFlag":"YWYyNWMxOWQ1ZTY4ZmJhOQ==","UserParam":"","Version":"V1.0.0"}\n'
    ws.send(szJson)
  }

  function WS_GetBCardNo() {
    const szJson = '{"Cmd":20400,"Head":"YZWL","IPFlag":"YWYyNWMxOWQ1ZTY4ZmJhOQ==","UserParam":"","Version":"V1.0.0"}\n'
    ws.send(szJson)
  }
  function WS_GetDeviceNo() {
    const szJson = '{"Cmd":20510,"Head":"YZWL","IPFlag":"YWYyNWMxOWQ1ZTY4ZmJhOQ==","UserParam":"","Version":"V1.0.0"}\n'
    ws.send(szJson)
  }

  function WS_ReadInfo() {
    cleanMsg()
    const szJson = '{"Cmd":10000,"Head":"YZWL","IPFlag":"MGEyZmU1NmY5ODZlZGMyNg==","UserParam":"eyJBcHBLZXkiOiI5OWZmYjJmOThhMjkwNzExMDdjN2EwOWFkMmM2ZDA5NiIsIkRlY29kZVBob3RvIjp0cnVlLCJGYWNlQ29tcGFyZSI6ZmFsc2UsIlBob3RvRm9ybWF0IjoxLCJTZXJ2ZXJJUCI6ImlkLnl6ZnV0dXJlLmNuIiwiU2VydmVyUG9ydCI6ODg0OH0NCg==","Version":"V1.0.0"}\n'
    ws.send(szJson)
  }

  function Http_ReadInfo() {
    cleanMsg()
    const webUrl = 'http://' + connectAddress.val() + '/api/info'
    $.ajax({
      url: webUrl,
      type: 'GET',
      dataType: 'json',
      success: function (result) {
        processContent.text('web api接口:' + webUrl + ' 读取身份证信息成功')
        const szparam = result
        setDocumentInfo(szparam);
      },
      error: function (jqXHR, textStatus, errorThrown) {
        processContent.text('web api接口:' + webUrl + ' 读取身份证失败,原因:' + hex2a(window.atob(errorThrown)))
      }
    })
  }
  function Http_GetASN() {
    const webUrl = 'http://' + connectAddress.val() + '/api/asn'
    $.ajax({
      url: webUrl,
      type: 'GET',
      dataType: 'json',
      success: function (result) {
        const szparam = result
        processContent.text('web api接口:' + webUrl + ' 读取成功。 A卡SN:' + szparam.SN)
      },
      error: function (jqXHR, textStatus, errorThrown) {
        processContent.text('web api接口:' + webUrl + ' 读取A卡SN失败,原因:' + hex2a(window.atob(errorThrown)))
      }
    })
  }
  function Http_GetBCardNo() {
    const webUrl = 'http://' + connectAddress.val() + '/api/bsn'
    $.ajax({
      url: webUrl,
      type: 'GET',
      dataType: 'json',
      success: function (result) {
        const szparam = result
        processContent.text('web api接口:' + webUrl + ' 读取成功。 身份证卡片SN:' + szparam.SN)
      },
      error: function (jqXHR, textStatus, errorThrown) {
        processContent.text('web api接口:' + webUrl + ' 读取身份证卡片SN失败,原因:' + hex2a(window.atob(errorThrown)))
      }
    })
  }
  function Http_GetDeviceNo() {
    const webUrl = 'http://' + connectAddress.val() + '/api/devsn'
    $.ajax({
      url: webUrl,
      type: 'GET',
      dataType: 'json',
      success: function (result) {
        const szparam = result
        processContent.text('web api接口:' + webUrl + ' 读取成功。 读卡器芯唯一号:' + szparam.SN)
      },
      error: function (jqXHR, textStatus, errorThrown) {
        processContent.text('web api接口:' + webUrl + ' 读取读卡器芯唯一号失败,原因:' + hex2a(window.atob(errorThrown)))
      }
    })
  }

  function parseDateString(str, deco, zero) {
    let year = str.substr(0,4)
    let month = str.substr(4,2)
    let date = str.substr(6)
    if(zero) {
      month = month.substr(0,1) === "0" ? month.substr(1) : month
      date = date.substr(0,1) === "0" ? date.substr(1) : date
    }
    return `${year}${deco}${month}${deco}${date}`
  }
  </script>
<script>
function cyberwin_仙盟创梦_初始化本体(){
	  var btnOnWS  = $('#on-websocket')
  var btnOffWS = $('#off-websocket')

  var btnWS_ID       = $('#websocket-ID')
  var btnWS_IDSN     = $('#websocket-ID-sn')
  var btnWS_ASN      = $('#websocket-A-sn')
  var btnWS_DeviceNo = $('#websocket-device-No')

  var btnHttp_ID       = $('#http-ID')
  var btnHttp_IDSN     = $('#http-ID-sn')
  var btnHttp_ASN      = $('#http-A-sn')
  var btnHttp_DeviceNo = $('#http-device-No')

  var connectAddress  = $('#connect-address')
  var processContent  = $('#process-content')
  var btnCleanProcess = $('#clean-process')

  var cardFront  = $('#card-front')
  var cardBack   = $('#card-back')

  var SNContent  = $('#SN-content')
  var image      = $('#image')
  var name       = $('#name')
  var sex        = $('#sex')
  var nation     = $('#nation')
  var year       = $('#year')
  var month      = $('#month')
  var date       = $('#date')
  var address    = $('#address')
  var number     = $('#number')
  var department = $('#department')
  var expiry     = $('#expiry')

	
  btnOnWS.on('click', conWS)
  btnOffWS.on('click', disconWS)

  btnWS_ID.on('click', WS_ReadInfo)
  btnWS_IDSN.on('click', WS_GetBCardNo)
  btnWS_ASN.on('click', WS_GetASN)
  btnWS_DeviceNo.on('click', WS_GetDeviceNo)

  btnHttp_ID.on('click', Http_ReadInfo)
  btnHttp_IDSN.on('click', Http_GetBCardNo)
  btnHttp_ASN.on('click', Http_GetASN)
  btnHttp_DeviceNo.on('click', Http_GetDeviceNo)

  btnCleanProcess.on('click', cleanMsg)

  processContent.text('本demo支持websocket和webapi两种网页调用方式')
	}

</script>

未来之窗 - 鱼住未来身份识别服务 Demo 技术解析与应用场景

一、Demo 核心功能概述

该 Demo 展示了鱼住未来身份识别服务的两种核心调用方式,通过 Websocket 和 Web API 实现对身份证、A 卡等证件信息的读取与解析,主要功能包括:

  • 双协议支持:同时兼容 WebSocket 长连接与 HTTP 短连接两种通信协议
  • 多证件类型识别:支持身份证、港澳台居民居住证、新旧版外国人永久居留证等多种证件
  • 信息可视化展示:将读取的证件信息(姓名、性别、地址等)结构化展示在页面中
  • 主动 / 被动交互模式:WebSocket 支持读卡器刷卡自动推送数据,也可主动发送请求
二、技术架构与实现细节
1. 页面布局与样式设计

采用 Flex 弹性布局实现响应式界面,主要分为三大功能区块:

  • 功能操作区:提供连接配置、协议选择(WebSocket/HTTP)、功能按钮
  • 过程日志区:实时显示通信过程与错误信息
  • 结果展示区:以卡片形式可视化证件信息,支持正反面切换

核心 CSS 布局类说明:

css

复制代码
.flex { display: flex; } /* 基础弹性布局 */
.flex-direction { flex-direction: column; } /* 垂直排列 */
.justify-between { justify-content: space-between; } /* 两端对齐 */
.align-center { align-items: center; } /* 垂直居中 */
.basis-df { flex-basis: 50%; } /* 占据50%宽度 */
2. WebSocket 通信实现

通过new WebSocket()创建长连接,核心事件处理:

  • onopen:连接成功后显示操作指引
  • onmessage:解析二进制数据并转换为 JSON 格式
  • onclose:断开连接时提示状态

javascript

复制代码
// WebSocket连接函数
function conWS() {
  const webUrl = 'ws://' + connectAddress.val() + '/ws';
  ws = new WebSocket(webUrl);
  ws.onmessage = function(messageEvent) {
    const jsonobject = JSON.parse(messageEvent.data);
    if (jsonobject.Ret == 0) {
      // 成功处理逻辑,调用证件解析函数
      const szparam = JSON.parse(window.atob(jsonobject.UserParam));
      setDocumentInfo(szparam);
    }
  }
}
3. Web API 接口调用

基于 jQuery.ajax 实现 HTTP 请求,支持 GET 方式获取数据:

javascript

复制代码
// Web API读取身份证信息
function Http_ReadInfo() {
  const webUrl = 'http://' + connectAddress.val() + '/api/info';
  $.ajax({
    url: webUrl,
    success: function(result) {
      const szparam = result;
      setDocumentInfo(szparam); // 统一使用证件解析函数
    }
  });
}
4. 证件信息解析处理

通过hex2a函数解码二进制数据,根据CardType字段区分证件类型:

  • 74:港澳台居民居住证
  • 73:旧版外国人永久居留证
  • 89:新版外国人永久居留证
  • 其他:中国大陆身份证

javascript

复制代码
// 核心解析函数
function setDocumentInfo(szparam) {
  if (szparam.CardType == 74) {
    // 港澳台证件样式处理
    cardFront.addClass('card-hongkong-macao-taiwan-front');
    // 填充姓名、性别等信息
    cardFront.find('.name').text(hex2a(window.atob(szparam.CardInfo.Name)));
  } 
  // 其他证件类型处理逻辑...
}
三、核心技术亮点
  1. 双协议兼容性设计

    • WebSocket 适合需要实时推送的场景(如读卡器刷卡自动通知)
    • Web API 适合短连接请求(如单次信息查询)
  2. 多证件类型适配

    • 通过CardType字段动态切换界面样式
    • 统一解析函数处理不同证件数据结构
  3. 数据可视化展示

    • 模拟真实证件布局,区分正反面显示
    • 照片以 Base64 格式直接渲染
四、应用场景与扩展方向
典型应用场景:
  • 酒店入住登记系统:通过读卡器快速读取身份证信息
  • 机场安检身份核验:实时获取证件信息与数据库比对
  • 政务服务自助终端:支持多类型证件办理业务
  • 企业访客管理系统:非接触式读取访客证件
技术扩展方向:
  1. 增加人脸识别功能,实现人证比对
  2. 集成 OCR 技术,支持纸质证件扫描识别
  3. 加入加密传输模块,提升数据安全性
  4. 开发移动端 SDK,支持 Android/iOS 设备
五、部署与使用说明
  1. 环境要求

    • 浏览器支持 WebSocket(现代浏览器均支持)
    • 后端服务地址配置(默认127.0.0.1:30004
    • 读卡器硬件连接正常
  2. 操作流程

    • 配置服务地址
    • 选择协议(WebSocket 需先连接)
    • 点击对应按钮读取证件信息
    • 查看右侧结果展示区

该 Demo 通过简洁的界面与清晰的代码结构,展示了身份识别服务在 Web 端的完整实现方案,为集成各类证件读取功能提供了可复用的技术框架。

相关推荐
芬兰y5 分钟前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁12 分钟前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry12 分钟前
Fetch 笔记
前端·javascript
拾光拾趣录13 分钟前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟14 分钟前
vue3,你看setup设计详解,也是个人才
前端
Lefan18 分钟前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson23 分钟前
青苔漫染待客迟
前端·设计模式·架构
写不出来就跑路44 分钟前
基于 Vue 3 的智能聊天界面实现:从 UI 到流式响应全解析
前端·vue.js·ui
OpenTiny社区1 小时前
盘点字体性能优化方案
前端·javascript
FogLetter1 小时前
深入浅出React Hooks:useEffect那些事儿
前端·javascript