
简介一份围绕名片创建与交换场景的微信小程序模板源码面向初级小程序开发者或需要快速搭建电子名片应用的商务/个人用户。源码包含用户认证、名片编辑器、信息存储、分享功能与模板管理等核心模块基于微信官方开发框架实现涉及WXML、WXSS、JavaScript及配置文件。包内共30个文件涵盖8个png图标、6个json配置、5个js逻辑、4个wxss样式、4个wxml页面及说明文档与许可证整体仅64KB结构轻量。已有158人学习下载。开发者可通过源码掌握小程序页面布局、样式编写、云存储调用与分享接口等实践要点便于在此基础上扩展为企业电子名片或创意个人名片。1. 名片模板的坑难点在新建链路不在卡片 UI微信小程序里做名片模板很多人以为难点是把那张名片卡片做得好看真正动手才发现静态展示页面用假数据就能糊弄过去反复重做的是新建名片这条链路表单字段怎么设计、手机号怎么校验、头像二维码图片怎么持久化、新加的记录怎么回到列表。标题里那个 zip 模板源码核心价值就是把这条链路提前封装好。适合正在搭商务社交类 MVP 的开发者也适合手里有 uniapp 经验、想快速给现有项目加一个名片模块的人。建议拿到压缩包先别急着跑先看app.json的pages数组和utils目录里有没有 storage 封装这决定了你后面要花多少时间拆代码。2. 拆解名片模板源码目录、数据模型与渲染边界2.1 典型目录结构先分清页面与工具模块常见名片模板包解压后目录长这样. ├── app.js ├── app.json ├── app.wxss ├── pages/ │ ├── index/ # 名片列表 / 首页 │ ├── add/ # 新建名片表单页 │ └── detail/ # 名片详情页 ├── utils/ │ ├── storage.js # 本地存储封装 │ ├── validate.js # 表单校验 │ └── format.js # 字段格式处理 ├── components/ │ └── card-item/ # 名片卡片组件 └── images/判断一个模板靠不靠谱看三条utils/storage.js是否统一封装了setStorageSync和getStorageSyncpages/add是否独立成页而不是堆在 index 页面里app.json的pages数组第一项是不是你希望用户第一眼看到的页面。这三条都满足后面改造才不会牵一发动全身。提示微信开发者工具不支持直接导入 zip也不能只双击某个页面文件单独编译。先把 zip 解压成普通文件夹再选「导入项目」目录指到包含app.json的那一层。路径关系最容易在 copy 阶段出错。页面的pages/目录如果手改了一层但app.json里还是老路径wx.navigateTo会一直报page not found。这类问题跟页面代码无关就是路径字符串对不齐。所以无论模板源码来自哪里第一件事是让pages数组和实际目录一一对上。2.2 数据模型字段不该拍脑袋定新建名片页一张表单背后是一个对象。把字段类型定清楚比先做 UI 重要。名片模板常见字段如下字段类型用途是否必填idstring唯一标识用于列表 key 和分享直达是namestring姓名是companystring公司否titlestring职位否phonestring手机号建议是emailstring邮箱否addressstring地址否avatarstring头像图片路径否qrcodestring微信二维码图片路径否remarkstring备注否createdAtnumber创建时间戳用于排序是phone 是否必填取决于产品场景。交换名片后快速录入手机号是唯一能联系到对方的字段建议必填如果是 CRM 内部线索管理name 必填就够不要为了表单好看把负担做重。一个好的名片模板不是字段多而是每个字段出现在合适的位置。2.3 模板语言WXML 数据绑定和事件分发微信小程序用的是 WXML 模板语言界面上的输入框通过value绑定 data通过bindinput把用户输入写回。新建名片页常见写法input classform-input value{{form.name}} >onFieldInput(e) { const field e.currentTarget.dataset.field; this.setData({ [form.${field}]: e.detail.value }); }用>input :valueform.name inputonFieldInput /事件名和原生bindinput不一样。这决定了后面所有页面改造的方式原生模板直接在微信开发者工具里改uniapp 模板在 HBuilderX 里改再运行到微信开发者工具看效果。两种项目都能实现新建名片但第 3 章的跑通路径不一样不要混用。3. 用微信开发者工具跑通模板导入、报错与最小新建代码3.1 导入 zip 的正确姿势与两个常见误操作拿到名片模板源码下载.zip后直接在微信开发者工具点「导入项目」再选 zip 文件一定会报app.json找不到。正确顺序是先解压再导入文件夹。另外别把pages/add当成项目根目录根目录必须同时包含app.js、app.json、app.wxss。具体步骤把 zip 解压到不含中文、不留空格的路径例如D:\work\card-template。微信开发者工具选「导入项目」目录选解压后的根目录。AppID 先用「测试号」调wx.getUserProfile、云开发或真实请求时再换正式 AppID。导入后白屏先看 Console 报错再回头看app.json的pages数组是否指向了不存在的文件。常见的报错与处理方式报错 / 现象原因处理app.json: 未找到目录选错层级选根目录重新导入page not foundpages路径与实际目录不一致逐个核对 app.json 路径thirdScriptError基础库版本过低ES6 被拦详情-本地设置调高调试基础库component is not found页面 json 缺 usingComponents在对应页面 json 补组件声明canvas 相关报错用了旧版 canvas 接口改到 canvas 2d 接口见 5.13.2 最小新建名片代码表单收集、校验与存储模板源码最值得复用的是「新建即存」这套逻辑。最小集合只需add.wxml、add.js、utils/storage.js。提交逻辑如下// pages/add/add.js const store require(../../utils/storage); Page({ data: { form: { name: , phone: , company: , title: } }, onFieldInput(e) { const field e.currentTarget.dataset.field; this.setData({ [form.${field}]: e.detail.value }); }, submitForm() { const name this.data.form.name.trim(); const phone this.data.form.phone.trim(); if (!name) { wx.showToast({ title: 姓名不能为空, icon: none }); return; } if (phone !/^1\d{10}$/.test(phone)) { wx.showToast({ title: 手机号格式不对, icon: none }); return; } const card { id: ${Date.now()}_${Math.random().toString(36).slice(2, 6)}, ...this.data.form, createdAt: Date.now() }; const list store.getList(); list.unshift(card); store.setList(list); wx.showToast({ title: 已保存 }); setTimeout(() wx.navigateBack(), 600); } });逻辑说明id用时间戳加随机串拼接是因为Date.now()在同一批快速提交里可能重复而wx:keyid对重名 id 会告警或渲染错乱。list.unshift把新名片放到数组头部列表页直接按数组序遍历就等价于按最近创建排序。store.getList()内部实现对应第 2 章的 storage 封装读取时兜一层数组判断会更安全第 4 章会展开。参数细节正则^1\d{10}$只判 11 位数字开头不做号段区分。配合input组件的typenumber和maxlength11键盘会弹数字键盘超长内容无法输入。模板如果没限制maxlength用户多打一位会被正则拦下体验就差了一截。3.3 uniapp 模板的另一个启动路径解压后如果看到pages.json和main.js这是 uniapp 的微信小程序模板。正确启动方式是用 HBuilderX 打开项目根目录。菜单「运行」→「运行到小程序模拟器」→「微信开发者工具」。首次运行前在 HBuilderX 设置里填好「微信开发者工具」的可执行文件路径路径不对会提示连不上微信开发者工具。HBuilderX 运行到小程序后产物在unpackage/dist/dev/mp-weixin目录下这里才有编译后的app.json。不要直接拿src目录去微信开发者工具导入uniapp 源码里的组件、main.js都不能被小程序运行时直接解析。判断标准很直接根目录文件是main.js还是app.js。是main.js就走 HBuilderX别在微信开发者工具里硬刚。这个坑排在微信小程序项目实例相关搜索前面很多年因为 uniapp 模板源码 zip 非常普遍。3.4 图片上传临时路径必须先持久化名片模板往往带头像、二维码。wx.chooseMedia选到的图片路径通常是http://tmp/xxx.jpg这是临时文件小程序进程冷启动后可能失效。如果把临时路径直接存进card_list当时列表页能显示第二天再看就裂图。改动最小的处理是选图后立刻复制到用户目录wx.chooseMedia({ count: 1, mediaType: [image], sourceType: [album, camera], success: (res) { const tmpPath res.tempFiles[0].tempFilePath; wx.getFileSystemManager().saveFile({ tempFilePath: tmpPath, success: (saveRes) { this.setData({ form.avatar: saveRes.savedFilePath }); } }); } });saveFile适合图片小、数量有限的本地场景。如果模板要支持多设备同步直接换成wx.cloud.uploadFile返回 fileID再把 fileID 存进名片记录。不要把wx.chooseMedia返回的临时路径当成正式数据这是模板二次开发最常见的坑之一。4. 把新建名片做成能用的功能校验、持久化与启动页4.1 三层校验从 if 套 if 到规则表新手模板里常见的是 if 套 if字段少时还能看名片字段一多就很容易漏判。我一般改成规则数组const rules [ { key: name, test: (v) v.trim().length 0, msg: 请填写姓名 }, { key: phone, test: (v) /^1\d{10}$/.test(v), msg: 手机号格式不正确 } ]; checkForm(form) { for (const rule of rules) { if (!rule.test(form[rule.key] || )) { wx.showToast({ title: rule.msg, icon: none }); return false; } } return true; }字段更多、规则更复杂时再引入WxValidate这类声明式校验库。模板如果自带校验工具就复用不要在真机上重复造边界条件。校验时机推荐「失焦校验 提交兜底」失焦校验在bindblur里做只提示当前字段提交时再全量跑一遍。如果只做提交校验用户填十项只有一项错会被连续 toast 轰炸好几轮。4.2 本地存储还是云开发按容量和同步需求选方式优点缺点适用场景wx.setStorageSync零成本、离线可用单机存储、约 10MB 上限个人名片夹、演示模板云开发数据库多端同步、可查询需开通云环境、有配额限制名片需要多设备共享自建后端 API能力最大需要维护服务端已有账号体系的团队模板默认本地存储没问题。但要加分享名片给对方对方直接存进通讯录之类能力时就要意识到数据存在本地分享后对方拿不到。商务交换场景的合理改造顺序是先跑通本地单机版再把store.setList内部替换成wx.cloud.database().collection(cards).add列表页getList换成查询接口。页面逻辑不动只动数据访问层回归成本最低。4.3 修改刚进入的加载页面小程序打开后先看到哪个页面这个问题答案写在app.json里{ pages: [ pages/add/index, pages/index/index, pages/detail/index ] }pages数组第一项就是启动页。但要注意改这里不生效时去看app.js的onLaunch里有没有wx.reLaunch或条件跳转。很多模板把未登录跳登录页已登录跳首页的逻辑放在启动流程里此时改pages顺序会被覆盖。如果想更精细地控制加载过程可以单独做一个loading页作为第一项在onLoad里做数据准备然后wx.reLaunch到名片首页。代价是用户每次启动都多一次页面过渡闪烁。大部分场景里直接把新建页设为首页反而更直接。4.4 长按拖拽排序先做长按置顶别急着上拖拽库名片多了以后用户会想自己调整顺序。最简单的实现不是真拖拽而是长按触发置顶onCardLongPress(e) { const id e.currentTarget.dataset.id; const list this.data.list; const index list.findIndex((item) item.id id); const [card] list.splice(index, 1); list.unshift(card); store.setList(list); this.setData({ list }); }这不是真正意义的免费拖拽但用最少代码解决了把某人放在最前面的核心诉求。微信小程序长按拖拽滚动的完整效果需要movable-area或第三方 sortable 组件引入依赖就得考虑压缩体积和不同机型的触摸冲突。我建议模板先把长按置顶做掉用户真的需要拖拽时再升级。这里最适合配radio-group让用户在「最近创建优先」和「手动置顶优先」之间单选一种排序规则比同时维护两套逻辑省很多事模板的列表页在上手阶段也不会显得太乱。4.5 读取时的防呆别让一条脏数据挂掉整个列表本地存储的读取封装要兜住异常function getList() { const raw wx.getStorageSync(card_list); if (!raw) return []; try { const parsed JSON.parse(raw); return Array.isArray(parsed) ? parsed : []; } catch (e) { return []; } }模板源码里如果没有Array.isArray这层某次写入坏掉后读取返回{}列表页this.data.list.length是undefined渲染层不报错但列表永远空白。这层防呆放在数据访问层属于一次改造、长期受益比在页面里每次if (Array.isArray(list))优雅得多。5. 模板二次开发canvas 名片海报与回归验证5.1 用 canvas 生成可保存的名片海报名片模板的最终交付经常是把名片做成一张图让用户保存到相册。实现上别用截屏接口直接画 canvas 2dconst query wx.createSelectorQuery(); query.select(#cardCanvas) .fields({ node: true, size: true }) .exec((res) { const canvas res[0].node; const ctx canvas.getContext(2d); const dpr wx.getSystemInfoSync().pixelRatio; canvas.width res[0].width * dpr; canvas.height res[0].height * dpr; ctx.scale(dpr, dpr); ctx.fillStyle #ffffff; ctx.fillRect(0, 0, res[0].width, res[0].height); ctx.fillStyle #333333; ctx.font 16px sans-serif; ctx.fillText(this.data.card.name || , 24, 80); });canvas 的坑集中在尺寸计算。节点宽高必须乘dpr否则导出图发虚画完用wx.canvasToTempFilePath导出再wx.saveImageToPhotosAlbum存相册。绘制内容从 data 里取别拿硬编码测试值出海报真机上很容易漏改。5.2 分享文案用模板字符串拼参数名片详情页加转发能力是模板做社交传播的关键一步。onShareAppMessage里用模板字符串拼参数onShareAppMessage() { return { title: ${this.data.card.name} 的名片, path: /pages/detail/index?id${this.data.card.id} }; }detail 页面onLoad(options)里用options.id去store.getList()找名片。注意 path 参数到options之后全是字符串比较 id 前先String()统一类型否则12和数字12对不上分享直达会变成详情页空白。5.3 改完模板后的回归清单复制模板改完功能后按顺序在真机验五件事空表单提交要能弹 toast手机号限制 11 位新建后返回列表新卡片在最上面杀进程重进列表和图片都还在转发到文件传输助手后点开能进入对应名片详情自定义导航栏时标题不被右上角胶囊遮挡。最后两项最值得重点测。分享参数不能只写path不带id否则点开是空详情页自定义导航的位置要用wx.getMenuButtonBoundingClientRect()计算顶部安全边距模板源码里只要改了navigationStyle为 custom这一步就是收尾必做项。本文还有配套的精品资源点击获取