公司打卡签到活动小程序,打卡更轻松,管理更便捷JavaScript 错误 - throw、try 和 catch

本文介绍了如何利用微信小程序创建活动打卡功能,包括设置地点、时间及趣味性元素,如AR签到、刷脸签到等。用户可以扫描二维码参与活动并在指定范围内打卡,打卡数据可用于排行榜,激发竞争。此外,还提到了周期性打卡活动的应用,如读书节,并举例说明了打卡活动在连锁店、工会活动等场景中的实际应用。
<script>
var txt="";
function message(){
	try {
		adddlert("Welcome guest!");
	}
	catch(err) {
		txt="本页有一个错误。\n\n";
		txt+="错误描述:" + err.message + "\n\n";
		txt+="点击确定继续。\n\n";
		alert(txt);
	}
}
</script>

微信是大家比较熟悉的社交软件,而依托于微信这个十亿多流量存在的小程序,则越来越多种多样,那么如何制作一个活动打卡小程序呢?线上签到打卡的玩法有哪些?

  在现实生活中,签到打卡我们可以使用手写签到,每个人在事先打印好的电子表格里,写下自己的姓名进行签到;或者可以在签到墙上进行签到,拍照打卡。

   除此之外,我们还可以进行电子签到打卡,电子签到的好处是简化信息登记的手续,便于活动现场的控场、信息传递以及活动后的客户跟进、调查反馈。AR签到、刷脸签到等则是在信息收集的基础上,尽可能把活动做得更有趣,满足大众的猎奇心理,同时达到活动传播的目的。

   下面我们介绍的这个,就是通过微信小程序的方式来实现的一个电子化的打卡方式,活动发起者,可以创建活动,设置好活动信息,然后转发给参与者进行打卡活动。参与者只要微信扫一扫活动的二维码即可加入参加活动。整个系统的使用方法介绍如下:

   首先,活动发起者,可以通过这个微信小程序,创建一场活动,然后在这场活动里设置一个或者多个点位供大家打卡签到。

   每个点位可以设置点位的介绍,设置这个地点的经纬度,以及需要在这个地点周围多少米范围内才能打卡签到。也可以设置签到的时间,比如是每天的8:00—18:00签到。

   用户进入到活动以后,只要走到了活动设置的点位周围指定的范围内,就可以实现打卡签到。在打卡签到的过程中,为了实现趣味性,还可以发表打卡心情,发文字配上图片。并可以获取自己的运动步数。根据运动步数进行排名等。

   同时还可查看本次打卡活动排行榜数据,排行榜是最能让成员了解自我的功能之一。通过排行榜,成员可以查看到自己与他人的数据排名状况,在了解目前打卡数据排名的同时,还可以激发成员之间良性竞争,使圈子中的打卡活动更加活跃有趣。
如下图是活动打卡签到小程序界面 : 

根据大家打卡签到完成的时间和完成的数量进行排名。完成数量越多排在越前,打卡签到的数量一样的,就看完成的时间,谁最先完成,就谁排在最前面。

上面介绍的是单次打卡活动的玩法,同时还可以创建周期打卡。比如读书节,可以在每个月组织一次这样的活动。每天打卡签到自己读书的心得,每天分享到活动里,然后月底,系统自动统计这一个月里,所有人打卡分享的天数。谁累计坚持打卡的天数最多,进行所有的排名。从高到低依次进行排名。、

整个系统支持用户自行创建活动,生成自己活动的二维码以及活动展示页面。创建者可以分享这个二维码到朋友圈,或者转发这个活动页面到微信群,其他人扫码或者点击即可进入到这个活动,参与并完成这个活动的打卡。

整个活动可以设置一个或者多个地点,所以如果是一个地点,那么就可以适合线下会议这种模式的打卡签到。如果是设置的多个点,就适合一些线下活动,需要集齐多个活动地点,打卡完毕获得活动的名次。

所有签到打卡的用户名单都可以导出成Excel表格。方便活动发起者管理。

 应用场景:

活动打卡适用于一些大型连锁店以及公司周年庆等。消费者只要到每个指定的门店,进行打卡签到,集齐打卡徽章,达到对应的数量,就可以领取奖品,兑换奖品。

还有一些线上打卡活动,比如通过每日至少一次发表绿色生活实践照片或视频的方式打卡,宣传绿色生活,增加参与感。活动有效时长连续七天,打卡内容展示“帮助环卫工人”“做环保公益活动”“送废纸废塑料去回收”等,旨在号召人们积极参与绿色生活实践。每日坚持打卡会有香囊、水杯、充电宝等奖品,以此鼓励环保打卡行为。

很多单位都喜欢使用这种的打卡活动来举办各种不同形式的线下互动活动。比如下面的:

厦门市总工会于5月1日至15日期间举办厦门市职工“绿色行”打卡活动,持有工会会员卡的职工任意前往5个首批厦门市职工“绿色行”打卡点拍摄照片即可参与活动,前200位经审核合格的参与对象将获得由厦门市总工会提供的礼品。

本次活动要求参与者上传5张在不同打卡点拍摄的照片,照片内容须包含打卡点明显标志且有参与者本人出现,每张照片大小在1M以内,像素清晰。照片上传数量不足、参与者与工会系统用户不一致、盗图都将被取消参与活动资格。经审核合格的参与对象将接到短信通知,5月20日后可登录厦门市总工会官网公告栏查看获奖人员名单。据介绍,活动通过打卡“绿色行”打卡点,让职工切身体会厦门的山海风貌和经济特区建设丰硕成果,以调动职工工作生活积极性,增强工会组织凝聚力和向心力。

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

如果我通过后端实现的话如何const express = require('express'); const sql = require('mssql'); const cors = require('cors'); const multer = require('multer'); const app = express(); // 配置 SQL Server 连接,使用 SQL 用户验证 const config = { user: 'admin', password: 'admin1', server: 'localhost', database: 'PYZK_KQ', options: { encrypt: false, trustServerCertificate: true } }; // 使用 cors 中间件 app.use(cors()); // 增加请求体大小限制 app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); // 增加字段值长度限制 const upload = multer({ limits: { fieldSize: 1024 * 1024 * 50 // 设置字段值最大长度为 50MB } }); app.use(express.static('public')); // 插入数据函数 async function insertData(data, tableName, columns) { let pool; try { if (!Array.isArray(data) || data.length === 0) { throw new Error('传入的数据不是有效的数组'); } pool = await sql.connect(config); const transaction = new sql.Transaction(pool); await transaction.begin(); for (const row of data) { const request = new sql.Request(transaction); columns.forEach((column) => { let value = row[column.name]; if (column.name === 'LeaveApplication') { if (value === '--' || value === null || value === 'NaN') { value = '无'; // 设置默认值 } else { value = String(value).slice(0, 100); // 确保长度不超过 100 个字符 } } else if (column.name === 'WorkingHours') { if (typeof value !== 'string') { value = String(value); } value = value.slice(0, 20); // 确保 WorkingHours 长度不超过 20 个字符 } else if (column.name === 'state') { value = value || null; // 如果 state 为空,则设置为 null } else { value = value === '--' ? null : value; } request.input(column.name, column.type, value); }); const columnNames = columns.map((col) => col.name).join(', '); const paramNames = columns.map((col) => `@${col.name}`).join(', '); await request.query(`INSERT INTO ${tableName} (${columnNames}) VALUES (${paramNames})`); } await transaction.commit(); return { message: `数据已成功插入 ${tableName} 表` }; } catch (error) { console.error(`插入数据到 ${tableName} 表时出错:`, error); if (transaction) await transaction.rollback(); throw error; } finally { if (pool) pool.close(); } } // 插入正常数据 app.post('/insertNormal', async (req, res) => { const tableName = 'NormalAttendance'; const columns = [ { name: 'Name', type: sql.NVarChar(100) }, { name: 'WorkingHours', type: sql.NVarChar(20) }, { name: 'Date', type: sql.Date }, { name: 'Shift', type: sql.NVarChar(50) }, { name: 'flag', type: sql.Int }, { name: 'dk1', type: sql.NVarChar(20) }, { name: 'dk2', type: sql.NVarChar(20) }, { name: 'dk3', type: sql.NVarChar(20) }, { name: 'dk4', type: sql.NVarChar(20) }, { name: 'dk5', type: sql.NVarChar(20) }, { name: 'dk6', type: sql.NVarChar(20) }, { name: 'dk7', type: sql.NVarChar(20) }, { name: 'dk8', type: sql.NVarChar(20) } ]; try { if (!Array.isArray(req.body)) { return res.status(400).json({ message: '传入的数据不是有效的数组' }); } const result = await insertData(req.body, tableName, columns); res.json(result); } catch (error) { res.status(500).json({ message: `插入正常数据时出错: ${error.message}` }); } }); // 插入异常数据 // 插入异常数据 app.post('/insertAbnormal', async (req, res) => { console.log('接收到插入异常数据的请求'); let pool; let transaction; try { const data = req.body; // 获取前端传来的数据 if (!Array.isArray(data) || data.length === 0) { return res.status(400).json({ message: '传入的数据不是有效的数组' }); } pool = await sql.connect(config); transaction = new sql.Transaction(pool); await transaction.begin(); const columns = [ { name: 'Name', type: sql.NVarChar(100) }, { name: 'Date', type: sql.Date }, { name: 'WorkingHours', type: sql.NVarChar(20) }, { name: 'Shift', type: sql.NVarChar(50) }, { name: 'AdjustedCheckInTime', type: sql.NVarChar(20) }, { name: 'LeaveApplication', type: sql.NVarChar(sql.MAX) }, { name: 'ActualCheckInTime', type: sql.NVarChar(20) }, { name: 'CheckInDate', type: sql.Date }, { name: 'flag', type: sql.Int }, { name: 'state', type: sql.NVarChar(50) } ]; for (const row of data) { const request = new sql.Request(transaction); columns.forEach((column) => { let value = row[column.name]; if (column.name === 'LeaveApplication' && (value === '--' || value === null || value === 'NaN')) { value = '无'; // 设置默认值 } else if (column.name === 'WorkingHours') { if (typeof value !== 'string') { value = String(value); } value = value.slice(0, 20); // 确保 WorkingHours 长度不超过 20 个字符 if (!value || /^\s*$/.test(value)) { value = '-/-'; } } else if (column.name === 'state') { value = value || '默认状态'; // 设置默认值 } else if (column.name === 'Date' || column.name === 'CheckInDate') { // 特别处理日期类型 if (value === '--' || value === null || value === 'NaN') { value = null; } else { // 尝试将日期转换为正确的格式 try { const date = new Date(value); if (isNaN(date.getTime())) { value = null; } else { // 确保日期格式正确 (YYYY-MM-DD) value = date.toISOString().split('T')[0]; } } catch (e) { console.warn(`日期转换错误: ${value}`, e); value = null; } } } else { value = value === '--' ? null : value; } request.input(column.name, column.type, value); }); const columnNames = columns.map((col) => col.name).join(', '); const paramNames = columns.map((col) => `@${col.name}`).join(', '); const insertQuery = `INSERT INTO [PYZK_KQ].[dbo].[YCSJ] (${columnNames}) VALUES (${paramNames})`; await request.query(insertQuery); } // 删除重复数据的 SQL 查询语句 const deleteQuery = ` WITH CTE AS ( SELECT [ID], [Name], [Date], [WorkingHours], [Shift], [AdjustedCheckInTime], [LeaveApplication], [ActualCheckInTime], [CheckInDate], [flag], [state], [Timestamp], ROW_NUMBER() OVER ( PARTITION BY [Name], [Date], [WorkingHours], [Shift], [AdjustedCheckInTime], [LeaveApplication], [ActualCheckInTime], [CheckInDate] ORDER BY CASE WHEN [state] <> '默认状态' THEN 0 ELSE 1 END, [ID] DESC ) AS RowNum FROM [PYZK_KQ].[dbo].[YCSJ] ) DELETE FROM CTE WHERE RowNum > 1; `; await new sql.Request(transaction).query(deleteQuery); // 获取去重后的数据 const result = await new sql.Request(transaction).query('SELECT [ID], [Name], [Date], [WorkingHours], [Shift], [AdjustedCheckInTime], [LeaveApplication], [ActualCheckInTime], [CheckInDate], [flag], [state], [Timestamp] FROM [PYZK_KQ].[dbo].[YCSJ]'); console.log('插入数据成功,返回去重后的数据'); await transaction.commit(); res.json(result.recordset); } catch (error) { console.error('插入异常数据时出错:', error); if (transaction) await transaction.rollback(); res.status(500).json({ message: `插入异常数据时出错: ${error.message}`, error: error.toString() }); } finally { if (pool) pool.close(); } }); // 获取打卡数据前端展示 app.get('/getAttendanceData', async (req, res) => { console.log(11111); let pool; try { pool = await sql.connect(config); const result = await pool.request().query('SELECT * FROM NormalAttendance'); // console.log(result); res.json(result.recordset); } catch (error) { res.status(500).json({ message: `获取数据时出错: ${error.message}` }); } finally { if (pool) pool.close(); } }); app.get('/getAttendanceData1', async (req, res) => { console.log(11111); let pool; try { pool = await sql.connect(config); const result = await pool.request().query(`SELECT * FROM YCSJ WHERE state <> '1'`); console.log(result); res.json(result.recordset); } catch (error) { res.status(500).json({ message: `获取数据时出错: ${error.message}` }); } finally { if (pool) pool.close(); } }); // 上传数据 app.post('/upload', upload.none(), async (req, res) => { let pool; let transaction; try { const data = JSON.parse(req.body.data); if (!Array.isArray(data) || data.length === 0) { return res.status(400).json({ message: '传入的数据不是有效的数组' }); } // 连接到 SQL Server pool = await sql.connect(config); transaction = new sql.Transaction(pool); await transaction.begin(); const tableName = 'PYZK_KQ_TEST'; // 获取数据库表的列名 const tableColumnsResult = await new sql.Request(transaction).query(`SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${tableName}'`); const tableColumns = tableColumnsResult.recordset.map(row => row.COLUMN_NAME); for (const row of data) { const rowColumns = Object.keys(row); const validColumns = rowColumns.filter(column => tableColumns.includes(column)); if (validColumns.length === 0) { continue; } const columnNames = validColumns.join(', '); const paramNames = validColumns.map((col, index) => `@param${index}`).join(', '); const insertQuery = `INSERT INTO ${tableName} (${columnNames}) VALUES (${paramNames})`; const request = new sql.Request(transaction); validColumns.forEach((column, index) => { const value = row[column] === '--' ? null : row[column]; request.input(`param${index}`, value); }); await request.query(insertQuery); } // 删除完全重复的行 const deleteQuery = ` WITH CTE AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY ${tableColumns.join(', ')} ORDER BY (SELECT NULL) ) AS RowNum FROM ${tableName} ) DELETE FROM CTE WHERE RowNum > 1; `; await new sql.Request(transaction).query(deleteQuery); await transaction.commit(); res.json({ message: '数据已成功插入 SQL Server 数据库,且已删除完全重复的行' }); } catch (error) { console.error('处理文件时出错:', error); if (transaction) await transaction.rollback(); res.status(500).json({ message: '处理文件时出错,请稍后重试' }); } finally { if (pool) pool.close(); } }); // 获取按人名排序后的数据,并转换日期时间列 app.get('/getSortedData', async (req, res) => { let pool; try { pool = await sql.connect(config); const tableName = 'PYZK_KQ_TEST'; // 获取数据库表的列名 const tableColumnsResult = await new sql.Request(pool).query(`SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${tableName}'`); const tableColumns = tableColumnsResult.recordset.map(row => row.COLUMN_NAME); // 构建查询语句 const query = ` SELECT 姓名, TRY_CONVERT(VARCHAR(10), TRY_CONVERT(DATE, LEFT(日期, 10)), 120) AS 日期, ${tableColumns.filter(col => col !== '日期' && col !== '姓名').join(', ')} FROM ${tableName} ORDER BY 姓名 `; const result = await new sql.Request(pool).query(query); res.json(result.recordset); } catch (error) { console.error('获取排序数据时出错:', error); res.status(500).json({ message: '获取排序数据时出错,请稍后重试' }); } finally { if (pool) pool.close(); } }); // 新 YCSJ 表并插入 NormalAttendance 表 app.post('/updateData', async (req, res) => { let pool; try { // 从请求体中获取所有参数,包括flag const { name, date, shift, workingHours, dk1, dk2, dk3, dk4, dk5, dk6, dk7, dk8, flag } = req.body; console.log('接收到的数据:', req.body); if (!name || !date || !shift || !workingHours) { return res.status(400).json({ message: '缺少必要参数' }); } pool = await sql.connect(config); const transaction = new sql.Transaction(pool); await transaction.begin(); try { // 新 YCSJ 表 const updateYCSJQuery = ` UPDATE YCSJ SET state = '1' -- 确保 state 是字符串类型 WHERE Name = @name AND Date = @date AND Shift = @shift `; const request1 = new sql.Request(transaction); request1.input('name', sql.NVarChar(100), name); request1.input('date', sql.Date, date); request1.input('shift', sql.NVarChar(50), shift); await request1.query(updateYCSJQuery); // 插入数据到 NormalAttendance 表 const insertNormalQuery = ` INSERT INTO NormalAttendance (Name, WorkingHours, Date, Shift, flag, dk1, dk2, dk3, dk4, dk5, dk6, dk7, dk8) VALUES (@name, @workingHours, @date, @shift, @flag, @dk1, @dk2, @dk3, @dk4, @dk5, @dk6, @dk7, @dk8) `; const request2 = new sql.Request(transaction); request2.input('name', sql.NVarChar(100), name); request2.input('workingHours', sql.NVarChar(20), workingHours); request2.input('date', sql.Date, date); request2.input('shift', sql.NVarChar(50), shift); request2.input('flag', sql.Int, flag); // 使用从请求中获取的flag值 request2.input('dk1', sql.NVarChar(20), dk1 || '--'); request2.input('dk2', sql.NVarChar(20), dk2 || '--'); request2.input('dk3', sql.NVarChar(20), dk3 || '--'); request2.input('dk4', sql.NVarChar(20), dk4 || '--'); request2.input('dk5', sql.NVarChar(20), dk5 || '--'); request2.input('dk6', sql.NVarChar(20), dk6 || '--'); request2.input('dk7', sql.NVarChar(20), dk7 || '--'); request2.input('dk8', sql.NVarChar(20), dk8 || '--'); await request2.query(insertNormalQuery); await transaction.commit(); res.json({ message: '数据插入成功' }); } catch (error) { await transaction.rollback(); throw error; } } catch (error) { console.error('新 YCSJ 表并插入 NormalAttendance 表时出错:', error); res.status(500).json({ message: `插入数据时出错: ${error.message}` }); } finally { if (pool) pool.close(); } }); // 辅助函数:根据 CheckInDate 分组签到时间 function groupCheckInTimesByDate(checkInTimes) { const groupedData = {}; checkInTimes.forEach(([time, date]) => { if (!groupedData[date]) { groupedData[date] = []; } groupedData[date].push(time); }); return groupedData; } const port = 3000; app.listen(port, () => console.log(`服务器运行在端口 ${port}`));
05-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值