简介:面向微信小程序开发学习者的“滴滴打卡”源码包,适合正在准备毕业设计或期末大作业的学生参考。资源以原生小程序结构组织,包含完整的前端页面、逻辑与样式代码,涵盖打卡类应用常见的功能模块与交互流程,可直接运行调试,也能供二次开发与功能扩展。整份资源共27个文件,其中以js逻辑脚本、wxml页面结构、wxss样式表和json配置文件为主,另有少量图片与说明文档,压缩包约164KB,体量精简便携。目前已有108人浏览学习。下载后可借助源码快速理解小程序从页面搭建、数据绑定到事件处理的完整实现方式,同时可结合README与演示截图对应查看实际效果,便于在毕设答辩或课程提交时快速梳理项目思路,无论是用于学习仿写还是作为项目底稿都较为合适。
1. 什么是“滴滴打卡”,为什么它适合当毕设课题
“滴滴打卡”这类微信小程序源码,在毕设和期末大作业里出现频率极高,本质上不是让你复刻一款打车软件,而是借“地图定位 + 签到打卡”这个完整业务闭环,把微信小程序的申请授权、地图渲染、云函数、数据库、统计展示串起来。老师想看到的是你掌握了小程序从页面到后端的一整条链路,而源码.zip 只是给了你一个起点。它的典型玩法是:用户打开小程序进入地图页,系统拿到经纬度后显示当前定位,点击“打卡”按钮写入一条带时间、位置、用户的签到记录,之后还能按日期查历史、看统计。适合前端基础一般、希望在一个月内跑通全栈项目的学生,也适合工程师快速迁移成企业内部考勤类应用。下面我会按一套最常见的原生小程序 + 云开发方案,把这套源码的关键模块拆开讲,从页面到云函数,再落到真机调试和答辩验收。
2. 拆解源码:搭建地图打卡的最小可用页面
2.1 技术选型:原生小程序还是 uni-app
先搞清楚手里这套源码用的是什么技术栈。标题如果是“微信小程序源码.zip”,常见有两种写法:一是微信开发者工具直接打开的原生小程序,二是用 uni-app 写的、需要 HBuilderX 导入后再编译到微信的工程。两者的核心 API 基本一致,但目录结构差别很大。
| 对比项 | 原生小程序 | uni-app |
|---|---|---|
| 开发工具 | 微信开发者工具 | HBuilderX + 微信开发者工具 |
| 代码运行 | 只跑微信 | 可编译到多端 |
| 地图组件 | 官方 map 组件 | 官方 map 封装,语法类似 |
| 云开发支持 | 原生支持最好 | 需要条件编译处理 |
| 毕设友好度 | 高,文档多 | 中等,多一层框架 |
我一般建议:如果是毕业设计,优先选原生小程序。原因是云开发(wx.cloud)在原生端的类型提示、真机调试、错误信息都最直接;用 uni-app 一旦遇到地图坐标或隐私接口问题,排查时要先判断是框架问题还是微信问题,耗时翻倍。如果打开源码发现是 uni-app 工程,也别急着换,把 pages.json 对到微信的 app.json,地图 API 的写法基本可以平移。
2.2 项目目录与页面配置
一个典型的打卡小程序,目录结构大概是这样的:
project ├── app.js ├── app.json ├── pages │ ├── clock │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── index.wxss │ └── record │ ├── index.js │ └── index.wxml └── cloudfunctions └── clockIn ├── index.js └── package.jsonapp.json 是入口配置,重点在权限声明和页面注册。很多源码打开后白屏或真机没反应,问题就出在这里:
{ "pages": [ "pages/clock/index", "pages/record/index" ], "permission": { "scope.userLocation": { "desc": "你的位置信息将用于打卡定位" } }, "requiredPrivateInfos": [ "getLocation" ], "cloud": true }参数说明:pages 数组第一项是启动页;permission 里的 scope.userLocation 描述了定位授权用途,desc 文案必须真实,否则审核会被拒;requiredPrivateInfos 是隐私接口声明,2023 年后这个字段缺一不可,它告诉微信“我的小程序确实要调 getLocation”;cloud 字段启用云开发能力,对应 app.js 里的 wx.cloud.init。
2.3 获取定位并把位置画到地图上
“滴滴打卡”的视觉重点是一张地图和地图中心的人头图标。原生小程序里直接用 map 组件,不需要引入第三方地图 SDK。
<map id="map" latitude="{{latitude}}" longitude="{{longitude}}" markers="{{markers}}" show-location ></map>配合的页面逻辑:
Page({ data: { latitude: 39.909, longitude: 116.397, markers: [], isClocking: false }, onLoad() { this.getLocation(); }, getLocation() { wx.getLocation({ type: 'gcj02', isHighAccuracy: true, success: (res) => { this.setData({ latitude: res.latitude, longitude: res.longitude, markers: [{ id: 1, latitude: res.latitude, longitude: res.longitude, iconPath: '/images/location.png', width: 32, height: 32 }] }); }, fail: () => { wx.showToast({ title: '定位失败,请检查定位权限', icon: 'none' }); } }); } });这里的关键参数是 type: 'gcj02'。微信地图组件默认使用国测局坐标,高德、腾讯地图也是 gcj02;如果你把 GPS 原始坐标直接塞进去,地图上的点会偏移几百米。isHighAccuracy: true 表示请求更高精度的定位,但要提醒一点,高精度模式在室内会明显增加耗电,如果做的是步频打卡而不是要求厘米级签到,可以改成 false。
打卡按钮放在地图底部,点击后获取当前时间,更新 markers 里的图标颜色,然后进入下一步的云函数调用。
3. 云开发:让打卡记录真正落库
3.1 初始化云开发环境
前端页面只是皮,打卡记录必须落到云数据库里。微信云开发的价值在于不用自己买服务器、不用配域名备案,控制台里点几下就有数据库和云函数。
在 app.js 中做初始化:
App({ onLaunch() { if (!wx.cloud) { console.error('请使用 2.2.3 或以上基础库以使用云能力'); } else { wx.cloud.init({ env: 'your-env-id', traceUser: true }); } } });env 参数是云环境 ID,在微信开发者工具“云开发”控制台里创建环境后可以看到,形如cloud1-xxxxxx。如果这里留空,默认使用第一个创建的环境,但多人协作时容易连错库。traceUser 设为 true 后,可以在数据库里看到每次调用的用户身份,排查 bug 时很有用。
一个常见误区:把环境和数据库集合混为一谈。环境是隔离的,集合是环境里的表。毕设项目只需要一个环境,但建议建两张表:一张clock_records存打卡记录,一张clock_users存用户额外信息。
3.2 云函数 clockIn:写入打卡记录
打卡动作的核心逻辑放在云函数里,好处是客户端传上来的时间、位置都不可信,云函数可以拿到微信认可的 openid 和服务器时间。
const cloud = require('wx-server-sdk'); cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }); const db = cloud.database(); exports.main = async (event) => { const { OPENID } = cloud.getWXContext(); const now = new Date(); const date = `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`; if (event.action === 'clock') { const record = { openid: OPENID, latitude: event.latitude, longitude: event.longitude, time: db.serverDate(), date }; const res = await db.collection('clock_records').add({ data: record }); return { code: 0, id: res._id, msg: '打卡成功' }; } if (event.action === 'list') { const res = await db.collection('clock_records') .where({ openid: OPENID, date: event.date }) .orderBy('time', 'desc') .limit(100) .get(); return { code: 0, list: res.data }; } };database 的 time 字段用的是 db.serverDate(),这是云数据库的服务器时间,不是用户手机时间。这样可以防止用户把手机时间改回昨天来补卡。date 字段冗余存储可读日期字符串,是为了查询时避免做时区转换,属于典型的以空间换时间。
前端调用:
wx.cloud.callFunction({ name: 'clockIn', data: { action: 'clock', latitude: this.data.latitude, longitude: this.data.longitude }, success: (res) => { if (res.result.code === 0) { wx.showToast({ title: '打卡成功', icon: 'success' }); } }, fail: (err) => { console.error('打卡失败', err); } });cloud.CloudID 不适合在这里用,因为我们需要在数据库里存 openid,而不是存临时凭证。云函数的 getWXContext() 拿到的 OPENID 是当前用户的唯一标识,后续查询打卡记录都靠它。
3.3 打卡记录查询与统计
记录页展示历史打卡,常见需求是“今天打了没有”“这个月打了几次”。在云函数里加一个聚合分支:
if (event.action === 'stats') { const $ = db.command.aggregate; const res = await db.collection('clock_records') .aggregate() .match({ openid: OPENID }) .group({ _id: '$date', count: $.sum(1) }) .sort({ _id: -1 }) .limit(30) .end(); return { code: 0, stats: res.list }; }聚合的要点在 group 阶段:_id 设为日期字段,count 用 sum(1) 对每天的记录计数。这个写法比先把所有记录拉到前端再数要省流量,也符合云开发查询上限 20 条的限制——聚合不会受单次 get 20 条限制影响,但要留意聚合结果默认最多返回 1000 条。
如果想实现“连续打卡天数”,可以用更简单的逻辑:把 stats 数组里的日期排序后,从今天往前遍历,有一次缺失就断裂。这个算法在云函数里跑很快,不需要引入额外的计算库。
4. 真机调试与高频踩坑
4.1 隐私协议:为什么 getLocation 在真机上首次白屏
很多源码在开发者工具里一切正常,一上真机,首页地图灰色、定位失败。大部分原因是隐私协议授权没处理。微信要求所有涉及位置、相册等隐私接口的调用,必须先通过wx.requirePrivacyAuthorize征得同意。
正确做法是在用户主动点击“打卡”时再触发授权,而不是在 onLoad 里弹窗:
handleClockTap() { wx.requirePrivacyAuthorize({ success: () => { this.getLocation(); this.submitClock(); }, fail: () => { wx.showToast({ title: '未同意隐私协议,无法打卡', icon: 'none' }); } }); }隐私接口声明和调用时机是两个坑:app.json 里漏了requiredPrivateInfos,即使代码正确也会报错;在 onLoad 里直接调 getLocation,则会因为“未响应用户操作”被拦截。答辩演示时,一定先从用户授权弹窗开始点,不要跳过程序启动的自动授权逻辑。
4.2 定位坐标偏差与打卡距离判断
“滴滴打卡”的项目名让人误以为定位越准越好,但实际做考勤类应用,关心的是“用户是否在允许范围内”。我一般在云函数里计算打卡点与公司坐标的距离,超出 200 米就拒绝。
function getDistance(lat1, lng1, lat2, lng2) { const rad = (d) => d * Math.PI / 180.0; const R = 6370996.81; const radLat1 = rad(lat1); const radLat2 = rad(lat2); const a = radLat1 - radLat2; const b = rad(lng1) - rad(lng2); const s = 2 * Math.asin(Math.sqrt( Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2) )); return Math.round(s * R); }距离计算中的 R 取 6370996.81 米,是常见的地球平均半径。阈值 200 米放在云函数而不是前端,因为前端改几行代码就能绕过限制。如果用的是高德或百度地图 SDK,还要处理坐标系转换,这里不再展开。
4.3 时间限制不能只靠前端
前端判断“8 点到 18 点才能打卡”只是用户体验层,真正的约束要在云函数里再做一次:
if (event.action === 'clock') { const now = new Date(); const hour = now.getHours(); if (hour < 8 || hour >= 18) { return { code: 403, msg: '不在允许打卡的时间段' }; } }注意这里用的是云函数所在服务器的时区。云开发环境默认是 UTC+8,但保险起见,建议在云函数里用new Date(Date.now() + 8 * 60 * 60 * 1000)转换成东八区时间,再拆出小时数。否则部署到非中国区环境时,时间判断会差 8 小时。
4.4 修改启动加载页与顶部导航栏适配
源码里如果第一眼看到一个加载页面,那是把pages/loading/index放在了 pages 数组第一位。这个加载页一般只做三件事:初始化云环境、静默检查登录态、然后跳转。跳转推荐用wx.reLaunch:
wx.reLaunch({ url: '/pages/clock/index' });如果想把顶部导航栏改成自定义的,需要在 app.json 对应页面的 window 里设置:
{ "navigationStyle": "custom" }自定义导航栏后,要给左上角返回箭头留出安全高度。推荐用wx.getWindowInfo().statusBarHeight获取状态栏高度,然后动态撑开顶部容器:
const info = wx.getWindowInfo(); this.setData({ statusBarHeight: info.statusBarHeight });statusBarHeight 在 iPhone 上是 44 左右,Android 是 24 左右,不要在 wxss 里写死像素值,否则不同机型会顶到状态栏里。
4.5 接口验参与抓包
做联调或答辩时,老师偶尔会问“你怎么证明数据传到后端了”。微信开发者工具的 Network 面板能看云函数的调用名,但看不见入参。最常见的做法是看云开发控制台的云函数日志,每次wx.cloud.callFunction都会打印一行日志,点进去能看到 event 对象和返回值。
如果你一定要抓 HTTPS 包,需要手机装 Charles 证书并开启代理,但云开发的请求走的是微信内部通道,抓到的是加密内容。与其纠结抓包,不如在云函数入口加一行日志:
console.log('clockIn event:', JSON.stringify(event));云函数日志里立刻能看到前端传上来的经纬度和 time 字段,验证链路比抓包快得多。
5. 超过及格线:给毕设加上图表和导出功能
5.1 用 canvas 画七日打卡柱状图
不引入 echarts 的情况下,用 canvas 画一个简单的周打卡图,足够撑起答辩页面。代码控制在 10 行核心逻辑内:
drawChart(list) { const ctx = wx.createCanvasContext('weekChart'); const width = 300; const height = 150; list.forEach((item, i) => { const barHeight = item.count * 20; ctx.setFillStyle('#1AAD19'); ctx.fillRect(20 + i * 38, height - barHeight, 24, barHeight); ctx.setFontSize(12); ctx.fillText(item.date.slice(5), 20 + i * 38, height - barHeight - 8); }); ctx.draw(); }list 数据来自云函数 stats 接口,item.count 是当天打卡次数。画布宽度 300、柱宽 24、柱间距 38,七个柱子正好铺满。答辩时强调这个图的数据源是云数据库聚合结果,不是写死的数据,就足够说明问题。
5.2 导出 CSV 打卡记录
老师通常不想登录你的数据库看记录,导出 CSV 是一种稳妥交付方式。在云函数里生成 CSV 并上传到云存储:
const csv = '日期,打卡时间,经度,纬度\n' + list .map(r => `${r.date},${r.time},${r.longitude},${r.latitude}`) .join('\n'); const uploadRes = await cloud.uploadFile({ cloudPath: `export/${date}.csv`, fileContent: Buffer.from(csv, 'utf8') }); return { code: 0, fileID: uploadRes.fileID };前端拿到 fileID 后,用wx.cloud.downloadFile下载到临时路径,再调用wx.openDocument让用户预览或转发到微信。注意 CSV 里的中文容易乱码,生成时可以在内容前面加上\uFEFFBOM 头,这样 Excel 打开时能正确识别 UTF-8。
验证方法很直接:在云开发控制台找到export目录,下载这个 CSV 看日期和经纬度是否和操作记录一致。这也是你答辩后给老师演示“数据闭环”的最好证据。
本文还有配套的精品资源,点击获取