☰
Vue项目接入华视身份证读卡器:从选型到部署的完整实践
2026/10/5 8:12:08 网站建设 项目流程

华视CVR系列身份证读卡器在政务窗口、酒店登记、园区访客、银行柜台这些场景里几乎随处可见。最近我在一个基于 Vue 的管理系统里接这台设备,翻遍了厂商 SDK 文档和网上零散帖子,发现大多数资料还停留在 ActiveX / IE 时代,要么就是只有一句“参考示例代码”,完全没法直接搬到现代浏览器项目里用。这篇文章把我这次从方案选型、本地中间件搭建,到 Vue 组件封装、联调部署的全过程完整捋一遍,顺便把路上踩过的坑记下来,给准备在 Vue 项目里接华视读卡器的朋友一条能直接走通的路径。

1. 项目概述与方案选型

1.1 华视读卡器是怎么把身份证信息读出来的

先搞清楚硬件这边的工作原理,后面所有代码设计才有依据。华视 CVR-100U 这类身份证读卡器,本质上是一个非接触式 IC 卡读写设备,外壳上有一个感应区域,把身份证放上去之后,读卡器通过射频天线给证件内的芯片供电并建立通信。

关键点在于,身份证芯片里的信息不是随便就能读出来的。芯片内部做了访问控制,读卡器里内置了一个经过认证的安全模块(SAM 模块),读取时要经过严格的双向认证流程。也就是说,不是任何一个 NFC 设备贴上身份证就能把姓名、身份证号这些数据读出来,必须是经过许可的读卡设备配合对应的安全授权机制。这个认证过程由读卡器和证件的硬件层完成,对我们上层应用开发者来说,只需要调用厂商 SDK 提供的接口,传入读卡器端口,SDK 会在内部完成认证和读取,最后以结构化数据的形式把信息返回来。

SDK 返回的信息通常包括:姓名、性别、民族、出生日期、住址、公民身份号码、签发机关、有效期限,以及一张证件照的 Base64 编码数据。不同型号和 SDK 版本字段名可能略有差异,但大体都是这套标准字段。掌握了这个底层的交互逻辑,后面设计中间件接口时就会清楚很多:我们要做的不是在浏览器里直接操作硬件,而是通过一层本地服务去调用 SDK,再把这个结果转成 HTTP / WebSocket 数据给前端用。

1.2 为什么现代 Vue 项目不能像老系统那样直接调读卡器

很多老旧系统接读卡器用的是 ActiveX 控件,厂商会提供一个 ocx 文件,在 IE 浏览器里注册后,页面里写<object>标签就能直接调用读卡函数。但这条路在今天的 Vue 项目里基本走不通。

原因有两个。第一,现代浏览器从 Chrome 45、Edge 等版本开始已经明确不再支持 ActiveX / NPAPI 插件,只有一个靠 IE 内核兼容模式才能勉强跑的方案,体验极差。第二,Vue 项目通常跑在 Chrome、Edge、Electron 这类环境里,页面和硬件之间隔着一道浏览器安全沙箱。浏览器不允许网页直接访问任意 USB 设备,即便使用 WebUSB API,也要求设备端点支持标准协议,而身份证读卡器走的驱动和通信协议不是通用 HID,WebUSB 很难直接适配,而且还要用户手动授权设备权限,对不擅长操作电脑的业务人员来说是很大的负担。

所以要在 Vue 里接华视读卡器,本质上就不是“纯前端”能搞定的事,我们必须换一个思路:把读卡器访问能力从浏览器中剥离出去,放到一个本地的、可以信任的进程里,再通过浏览器允许的网络接口(HTTP / WebSocket)暴露给前端页面调用。这是所有现代 Web 应用接本机硬件设备的通用解法,也是这篇文章方案的核心。

1.3 三种主流集成方案对比与选型

我在开始前把可行方案列了一遍,用下表做了对比,直接建议你照着选型思路走:

方案实现方式优点痛点适用场景
ActiveX / OCX 控件页面嵌入 ocx 插件老系统改造成本低只支持 IE,安全漏洞多,现代浏览器不可用老旧系统维护,不推荐新项目
WebUSB / WebHID 直接调设备浏览器原生 API 访问 USB无需装额外软件对读卡器驱动和协议要求高,授权流程繁琐实验性项目或特殊定制设备
本地中间件服务(HTTP / WS)本地程序调用厂商 SDK,再开放 HTTP / WebSocket 接口稳定可靠,跨版本兼容,对前端最友好需要安装和启动中间件程序大多数真实业务场景,推荐
Electron / 桌面壳整个应用套在 Electron 里可直接调用 Node 原生模块打包体积大,需要重构应用框架全桌面端应用,小团队自用

从表格能看出,唯一能兼顾现代前端体验和稳定硬件调用的,就是“本地中间件服务”方案。它的本质是用一个本机程序,比如几行代码写的 C# HttpListener 服务或者 Node.js HTTP 服务,去加载华视读卡器的厂商 DLL,然后把读卡结果包装成 JSON 接口,Vue 项目通过 axios 或 WebSocket 访问http://127.0.0.1:9527这个地址来完成读卡。这个方案在三年前、现在、以及未来两三年内,都是 Web 系统接身份证读卡器最稳妥的一条路。

2. 环境准备与中间件搭建

2.1 硬件、驱动与开发依赖清单

实操之前,先把环境清单列清楚。硬件方面我用的是华视 CVR-100U 这款 USB 口的读卡器,也是很多窗口单位最常见的一款。USB 口型号的好处是免外接电源,插到电脑后面板 USB 口就能用。如果你是其他型号,比如串口版,那么本地中间件初始化端口时需要改成对应的 COM 口号,逻辑是一样的。

驱动是必须装好的。Windows 下安装厂家提供的驱动后,在设备管理器里能看到一个“智能卡阅读器”或直接显示为厂商名称的设备节点。如果插上读卡器后提示“无法识别的 USB 设备”,先别急着继续开发,优先解决驱动问题。从我个人项目实战的经验来看,这一步没处理好,后面中间件即使写对了也会报“打开端口失败”。

开发依赖方面,需要准备的东西也不多:

  • 开发语言运行环境:如果中间件用 C# 写,装 .NET Framework 或 .NET 5+;如果用 Node.js 写,装 Node 18+。
  • Vue 项目环境:Vue 3 + Vite + axios,这部分是前端主战场。
  • 厂商 SDK:联系经销商或从官方获取华视读卡器二次开发资料,通常是 DLL 文件加一份接口说明文档。我用的版本提供的是一个标准 C 接口动态库,函数包括打开端口、读卡、关闭端口等。

这里单独提醒一句:不同批次 SDK 的 DLL 文件名可能不同,具体以你拿到的 SDK 文档为准。后面中间件代码里引入 DLL 时,务必把文件放到中间件程序同级目录或者系统 PATH 指定的目录下,否则运行时都会报找不到模块。

2.2 本地中间件的接口设计与数据协议

写中间件前先把接口定义想清楚。我的目标是让 Vue 端代码尽量简单,最好只暴露两个接口就能完成整个读卡流程:

  • GET /status:探测中间件是否运行、读卡器是否在线。返回{ code: 0, data: { readerOnline: true } }。
  • POST /readIdCard:触发一次读卡动作。读卡器持续感应,读到身份证后 SDK 内部认证并返回数据,接口同步返回完整信息。如果用户没放卡,可以设置超时时间,比如 15 秒后返回{ code: 1001, message: "timeout" }。

返回数据统一使用下面这种结构,方便 Vue 端做统一拦截处理:

{ "code": 0, "message": "success", "data": { "name": "张三", "gender": "男", "nation": "汉", "birthday": "19900101", "address": "某省某市某区某某路某号", "idNumber": "110101199001011234", "authority": "某市公安局某某分局", "validPeriod": "20150101-20350101", "photo": "/9j/4AAQSkZJRgABAQAAAQ==" } }

注意photo字段是证件照的 Base64 编码,有些 SDK 返回的是不带data:image/jpeg;base64,前缀的裸字符串,Vue 端渲染前需要自己拼上 MIME 头。字段名尽量对齐标准,避免在中间件这一层做过度定制,否则以后换读卡器品牌时前端还得跟着改。

2.3 中间件核心实现思路与代码骨架

中间件最省事的写法是用 C# 的 HttpListener 类,因为它不需要额外框架,可以直接在 Windows 上编译成 exe 跑。核心逻辑分三步:初始化读卡器端口、注册 HTTP 路由、在处理函数里调用 SDK 读卡。

我给出一个简化版的骨架:

using System; using System.IO; using System.Net; using System.Text; using System.Runtime.InteropServices; class Program { // 华视SDK提供的DLL导出函数,具体名称以官方文档为准 [DllImport("WuHanReader.dll", CallingConvention = CallingConvention.StdCall)] static extern int InitReader(int port); [DllImport("WuHanReader.dll", CallingConvention = CallingConvention.StdCall)] static extern int Authenticate(); [DllImport("WuHanReader.dll", CallingConvention = CallingConvention.StdCall)] static extern int ReadCardInfo(StringBuilder name, StringBuilder gender, StringBuilder nation, StringBuilder birthday, StringBuilder address, StringBuilder idNumber, StringBuilder authority, StringBuilder validPeriod); static void Main() { InitReader(1001); // 1001 通常是USB虚拟串口号,具体看文档 var listener = new HttpListener(); listener.Prefixes.Add("http://127.0.0.1:9527/"); listener.Start(); while (true) { var ctx = listener.GetContext(); var path = ctx.Request.Url.AbsolutePath; if (path == "/status") { WriteJson(ctx, "{\"code\":0,\"message\":\"success\"}"); } else if (path == "/readIdCard") { var error = Authenticate(); if (error != 0) { WriteJson(ctx, "{\"code\":1001,\"message\":\"read fail\"}"); continue; } // 读取字段并拼接JSON返回 // 注意:读卡成功后要调用CloseReader()释放端口 } ctx.Response.Close(); } } static void WriteJson(HttpListenerContext ctx, string json) { byte[] data = Encoding.UTF8.GetBytes(json); ctx.Response.ContentType = "application/json; charset=utf-8"; ctx.Response.OutputStream.Write(data, 0, data.Length); } }

如果你更熟悉 Node.js,也可以按同样的思路做一个 HTTP 服务:用 ffi-napi 加载 DLL,或者直接通过命令行调用厂商提供的读卡 demo 工具,再把 stdout 解析成 JSON。但说实话,在 Windows 上处理 DLL 调用这件事,C# 的 P/Invoke 机制比 Node 的 ffi 要稳得多,踩坑少,所以我建议优先用 C#。

这段骨架里留了几个口子没有展开,实际过程中有几个细节必须补上:每次读取完必须关闭端口,否则第二次读卡会失败;ReadCardInfo的参数应该用 StringBuilder 并预分配足够长度;读卡要设置超时逻辑,不能一直阻塞等待。这些细节跑到联调阶段全都会遇到,我在后面问题排查章节再展开讲。

3. Vue 端功能实现

3.1 封装读卡请求服务模块

中间件运行起来之后,Vue 端的技术难度其实不高了,核心是把读卡请求封装成一个独立的服务模块,别把 axios 调用散落在各个页面组件里。我先建一个src/api/reader.js,统一管理接口地址和请求逻辑。

中间件地址在开发环境和生产环境通常是同一个,都是http://127.0.0.1:9527,但为了后续部署灵活,我建议用 Vite 的环境变量来管理这个地址。.env.production里配置:

VITE_IDCARD_READER_BASE_URL=http://127.0.0.1:9527

然后在reader.js里读取:

import axios from 'axios' const service = axios.create({ baseURL: import.meta.env.VITE_IDCARD_READER_BASE_URL, timeout: 20000 }) export function getReaderStatus () { return service.get('/status') } export function readIdCard () { return service.post('/readIdCard') }

这里有个很实用的细节:axios 实例的超时时间不要设置得太短。读卡器是一个物理设备,用户把身份证放到感应区是需要时间的,尤其第一次使用的人反应会比较慢,我一般设置 20 秒,和中间件的超时时间保持一致。如果把 timeout 设置成 5 秒,用户还没来得及放卡,前端就报请求失败了,体验非常差。

3.2 页面组件与交互设计

读卡页面我用 Vue 3 的组合式 API 来写。界面交互很简单:一个按钮“读取身份证”,点击后调用readIdCard,拿到数据后自动填充到表单字段里。考虑实际使用场景,比如前台登记,工作人员把身份证放到读卡器上,系统自动识别并填单子,不需要手动逐字输入,所以交互设计的重点在“读卡中”的等待状态和“读卡失败”的反馈。

组件核心逻辑如下:

<template> <div class="idcard-reader"> <button :disabled="reading" @click="handleRead"> {{ reading ? '正在读卡,请把身份证放在感应区...' : '读取身份证' }} </button> <div v-if="form.name" class="idcard-photo"> <img :src="photoSrc" alt="证件照" /> </div> <el-form :model="form" label-width="90px"> <el-form-item label="姓名"> <el-input v-model="form.name" /> </el-form-item> <el-form-item label="性别"> <el-input v-model="form.gender" /> </el-form-item> <!-- 其他字段省略 --> </el-form> </div> </template> <script setup> import { reactive, ref, computed } from 'vue' import { ElMessage } from 'element-plus' import { readIdCard } from '@/api/reader' const reading = ref(false) const form = reactive({ name: '', gender: '', nation: '', birthday: '', address: '', idNumber: '', authority: '', validPeriod: '', photo: '' }) const photoSrc = computed(() => { return form.photo ? `data:image/jpeg;base64,${form.photo}` : '' }) async function handleRead () { reading.value = true try { const { data } = await readIdCard() if (data.code === 0) { Object.assign(form, data.data) ElMessage.success('读取成功') } else { ElMessage.error(data.message || '读取失败,请重试') } } catch (e) { ElMessage.error('无法连接读卡服务,请确认中间件已启动') } finally { reading.value = false } } </script>

看起来很简单,但真实场景里我会建议再加两个小功能。第一,读卡成功后给表单加一个轻微的闪烁高亮,提示操作人员数据已经填充完成。第二,读卡失败时如果中间件没起来,可以给出一个可点击的“检查读卡服务”链接,跳转到指导页面教用户怎么启动中间件。这些交互上的小设计,能在实际业务中明显降低客服压力,毕竟窗口工作人员出问题时第一反应是打电话找你。

3.3 身份证信息校验与脱敏处理

拿到了明文身份证信息之后,前端不能直接无差别展示和存储,必须做好校验与脱敏。这个环节很容易被忽略,但也是最见专业度的地方。

身份证号这一项,SDK 读出来的数据理论上是正确的,但从严谨角度还是要做一次 18 位格式校验和校验位验证。我写了个纯函数isValidIdCard,包含地区码、出生日期、校验位三段检查,这里不贴完整代码,核心逻辑是:前 17 位分别乘以不同权重后求和,再用 11 取模算出最后一位,与输入的末位对比。如果发现校验位不匹配,直接弹窗警告“读取数据异常”,引导重新读卡,避免脏数据进入业务库。

脱敏方面,我的建议是:详情页面必须展示完整姓名和完整身份证号,这是正常业务流程需要;但列表页面、日志系统、监控平台里统一做脱敏,比如110101********1234这种格式,姓名的中间字打星。注意,不要在前端 localStorage 里缓存完整的身份证信息和证件照,读卡数据应该只保存在当前这次操作的业务对象里,关闭页面后自动销毁。日志记录时也要过滤掉身份证号和照片字段。

这些点看起来小,但在等保测评和内部审计的时候都是加分项,而且能避免不少个人信息泄露的合规风险。

4. 实操联调与部署记录

4.1 本地联调的关键步骤

联调阶段我的习惯是三步走:先测中间件,再测页面,最后做异常演练。

第一步,先手动启动中间件 exe,然后用浏览器直接访问http://127.0.0.1:9527/status,能看到 JSON 返回说明服务没问题。接着用 Postman 调一次/readIdCard,把身份证放到读卡器上,确认返回的 JSON 字段完整、中文不乱码。这一步就把中间件层的问题全部排掉,不要直接开着 Vue 去调。

第二步,Vue 项目启动后,先确认浏览器能否跨域访问中间件。因为中间件端口是9527,Vue 开发服务器是5173,两者不同源。我的处理方式是在中间件响应里加上 CORS 响应头:

ctx.Response.Headers.Add("Access-Control-Allow-Origin", "*"); ctx.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); ctx.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");

如果你不想让中间件加 CORS 头,也可以在 Vite 里配置 proxy,把/reader-api代理到127.0.0.1:9527。两种方式我都试过,生产环境最终建议用 Nginx 反向代理而不是 CORS 放开,原因在下一小节讲。

第三步,异常演练。把读卡器的 USB 线拔掉,调一次接口看前端是否报错友好;把身份证从感应区拿走,读一次看超时提示是否正常;连续读 20 张卡,观察中间件进程是否内存上涨、端口是否释放。这套流程跑完,才能放心交付给客户。

4.2 生产环境部署的一个大坑:HTTPS 与混合内容限制

这是我这次项目踩过最深的坑,也最值得单独拿出来讲。正式环境里 Vue 项目通常部署在 Nginx 上,而且出于安全要求会强制开启 HTTPS。问题来了:HTTPS 页面里通过 axios 去请求http://127.0.0.1:9527,现代浏览器会直接拦截,报 Mixed Content 错误。业务人员打开页面,点击读卡按钮,控制台提示请求被阻止,中间件明明在跑也没用。

解决方案有三种,按推荐程度排:

第一种,在 Nginx 里配置反向代理,把/reader-api路径代理到http://127.0.0.1:9527。这样浏览器访问的地址是当前站点的 HTTPS 地址,属于同源请求,不存在混合内容问题,页面里 baseURL 也改成相对路径。这是最优解,对用户透明,也不影响中间件。

location /reader-api/ { proxy_pass http://127.0.0.1:9527/; proxy_set_header Host $host; }

第二种,中间件本身开启 HTTPS,用自签名证书并安装到 Windows 受信任的根证书颁发机构里。这个方案对运维要求高,证书过期维护麻烦,而且每台客户端电脑都要装证书,不推荐在几十台机器的场景推广。

第三种,中间件地址放在http://localhost下的子路径,配合同源不再受限的某些浏览器策略。但我实测下来不同浏览器行为不一致,不可靠。

所以聪明的做法是:前端代码里把VITE_IDCARD_READER_BASE_URL配置成空字符串或相对路径/reader-api,开发环境通过 Vite proxy 转发,生产环境通过 Nginx 反向代理转发。这样整套链路从页面上看就是同源请求,省去了一堆跨域和混合内容的麻烦。

4.3 读卡体验与性能细节优化

最后再优化一层体验。读卡接口是同步阻塞的,用户点击之后必须全神贯注等待结果。如果 20 秒超时,用户通常会直接关掉页面再开,这是实际业务中很常见的误操作。我建议的优化方案是:在页面上增加一个“自动读卡模式”开关。打开后,前端每隔 3 秒调用一次/readIdCard,读到数据就自动填充并退出轮询,读不到就继续轮询,直到用户手动关闭。这样工作人员就不用反复点按钮,把卡放上去,数据自动就出来了。

轮询会产生另一个问题:中间件每次读卡都会初始化端口、调用认证、再释放端口,这个动作如果频率太高,可能对读卡器寿命有影响。所以轮询间隔设置 3 到 5 秒比较合理,既不会让用户等太久,也不至于把硬件调用得过于频繁。我实际观察下来,连续高频调度 100 次后读卡器会有概率进入一种“假死”状态,必须重新拔插 USB 才能恢复,中间件代码里最好加一个请求限流或冷却时间。

证件照的 Base64 数据在身份证件里通常有几十 KB 到几百 KB,这个数据在 HTTP 传输时问题不大,但如果业务表要把照片存进数据库,建议在前端或服务端做一次压缩。我在实际项目里是把照片转为 WebP 格式后存储,体积能减少一半以上,而且对证件照这种固定色彩的照片,清晰度损失肉眼几乎看不出来。

5. 常见问题与排障技巧

5.1 读卡器识别失败:先查驱动再查端口

读卡器插上后没有任何反应,这个问题排在所有问题的第一位。我发现很多团队第一反应是翻代码,找人改中间件,但十次里有八次是驱动问题。正确排查顺序是:

  1. 看设备管理器里有没有异常设备,如果有黄色感叹号,说明驱动没装或者驱动版本不对。
  2. 用厂商自带的测试工具读卡一次,比如华视通常会附带一个“身份证阅读器检测工具”,如果这个工具也读不到,那肯定是驱动或硬件问题,跟代码无关。
  3. 如果测试工具能读,中间件却报“打开端口失败”,检查中间件初始化读卡器时用的端口号是否正确。USB 口的读卡器在 SDK 里通常被虚拟成一个串口,端口号可能是 1001 或者某个 COM 号,不同机器上可能不一样。我建议把端口号做成中间件配置文件,不要在代码里写死。

另外,USB 口供电不稳定也是一个容易忽略的问题。读卡器如果插在电脑前面板 USB 口或通过延长线连接,经常出现“偶尔能读、经常失败”的现象。建议插在机箱后面板主板的 USB 口,或者用一个带独立供电的 USB HUB。

5.2 浏览器与中间件通信失败的几类表现

前端调接口报跨域错误,这个最容易迷惑人。先说结论:本地中间件开发阶段可以临时放开 CORS,但生产环境必须用 Nginx 同源代理。如果你在开发环境遇到跨域问题,先把中间件的 CORS 响应头加上,这是最快的办法。

还有一类问题是安全软件拦截。Windows 自带的防火墙或者第三方杀毒软件,默认策略是阻止本机进程打开并监听 TCP 端口。现象是中间件进程明明运行,但浏览器访问http://127.0.0.1:9527/status直接超时。解决办法是把中间件 exe 加入防火墙白名单,或者首次启动弹窗时点击“允许访问”。我在一个客户现场就碰到过这种情况,排查了很久,最后发现是杀毒软件把中间件端口拦了,加白名单后一切正常。

混合内容限制前面详细讲过,这里再重复一次要点:HTTPS 页面请求 HTTP 本机接口,浏览器默认阻止。开发时如果因为临时证书问题想绕过,可以用 Chrome 的--allow-insecure-localhost启动参数,但只能作为本地调试手段,不能交付给客户用。

5.3 我自己踩过的几个值得记录的坑

第一个坑是端口释放问题。中间件第一次读卡成功后,第二次再读会卡住,提示“设备忙”。原因是 SDK 的ReadCard函数成功后,没有调用关闭端口函数。这不是每个人都会遇到,因为有些 SDK 内部会在超时后自动释放,但就有那么一个版本不会。解决办法很粗暴:每次读卡流程结束,不管成功失败,都在 finally 块里调用关闭端口函数,间隔 300 毫秒再允许下一次读卡。

第二个坑是返回数据里的日期格式不统一。有的 SDK 返回birthday是19900101,有的返回1990-01-01,还有的会直接把出生日期拆成年月日三个字段。如果前端直接展示,就会发现不同机器格式不一样。我在中间件层统一转成了YYYY-MM-DD格式,这样前端不管对接什么版本的读卡器,展示效果都一致。

第三个坑是测试卡和真实卡的数据差异。有些测试卡是模拟数据,地址栏可能只有“测试地址”四个字,签发机关也可能是空的。我在联调文档里专门注明:上线前必须用真卡在每台客户端电脑上验证一遍读卡流程,不要以为开发环境测过就万事大吉。真卡数据里有一些冷僻字,如果读码页的字符集不对,可能出现乱码。中间件返回 JSON 时一定统一使用 UTF-8,前端代码里不要再手动转码。

第四个坑是关于部署环境的。很多人以为后端代码部署在服务器上,读卡器中间件也应该配在服务器上。这完全是误区。身份证读卡器是物理硬件,必须插在使用者本地电脑上,所以中间件要装到每一台业务电脑上,而不是服务器。如果项目有 20 台客户端电脑,就要维护 20 套本机中间件,而且每台电脑的杀毒软件、系统版本都可能不同。建议做一个一键安装包,把驱动、中间件、快捷启动项一起打包,用 Inno Setup 或 NSIS 打包一下,不然光是帮客户逐台配置环境就够你忙一整天。

最后补一个实用的小建议

如果你接手的项目刚起步,还在纠结用什么技术方案,我个人的建议是不要一上来就写中间件代码,先确认厂商当前提供的 SDK 里有没有现成的 Web 中间件组件。有些版本的华视 SDK 会带一个“Web开发组件”,安装后会自动在本地开启一个固定端口,页面直接调它就行,省掉自己写中间件的一大堆事。这个方案适合业务简单、不想维护额外代码的团队。

但如果你的业务对读卡行为有定制需求,比如需要同时读卡并拍照、需要控制读卡超时时间、需要把照片直接上传到对象存储,那还是值得自己维护一套中间件。我这次项目里就是自己写的中间件,虽然前期多花了两天时间,但后面任何关于读卡的改动都变得非常可控。前端代码几乎没有变动,所有的硬件适配都收敛在中间件里,这大概就是模块化设计带来的好处吧。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询