手把手教你用zjy-calendar在uniapp里做一个高颜值打卡/签到日历(附完整代码)
手把手教你用zjy-calendar在uniapp里实现高颜值打卡日历在移动应用开发中打卡签到功能已经成为学习类、健身类和员工管理类App的标配功能。一个直观美观的日历界面不仅能提升用户体验还能有效增加用户粘性。本文将详细介绍如何使用uniapp生态中的zjy-calendar组件打造一个支持多状态标记、颜色自定义的高颜值打卡日历系统。1. 环境准备与组件安装首先确保你已经创建好uniapp项目。如果尚未创建可以通过HBuilderX的图形化界面或命令行工具快速初始化一个项目# 使用vue-cli创建uniapp项目 vue create -p dcloudio/uni-preset-vue my-calendar-projectzjy-calendar是uni-calendar的增强版组件提供了更丰富的样式自定义能力。安装步骤如下访问DCloud插件市场搜索zjy-calendar或直接访问插件页面点击下载插件ZIP按钮获取组件包将下载的ZIP包解压到项目的uni_modules目录下安装完成后你可以在项目的uni_modules/zjy-calendar目录下找到所有相关文件。这个组件最大的优势在于它完全兼容uni-calendar的API同时增加了dropColor和fontColor等实用属性。2. 基础日历功能实现让我们先实现一个最基本的日历展示功能。在页面中引入并使用zjy-calendar组件template view classcontainer zjy-calendar :selectedselectedDates changehandleDateChange monthSwitchhandleMonthSwitch / /view /template script import zjyCalendar from /uni_modules/zjy-calendar/components/zjy-calendar/zjy-calendar.vue export default { components: { zjyCalendar }, data() { return { selectedDates: [] } }, methods: { handleDateChange(e) { console.log(选中日期变化:, e) }, handleMonthSwitch(e) { console.log(月份切换:, e) } } } /script style .container { padding: 20rpx; } /style这段代码实现了基础日历展示日期选择事件监听月份切换事件监听selected属性用于设置默认选中的日期数组我们将在下一节详细讲解如何利用这个属性实现打卡状态标记。3. 打卡状态可视化实现zjy-calendar的核心优势在于其强大的状态标记能力。我们可以通过dropColor和fontColor属性为不同状态的日期设置不同的颜色标识。首先我们需要设计打卡数据的数据结构。通常一个打卡系统会有以下几种状态状态类型颜色标识说明已打卡#4CAF50用户当天已完成打卡未打卡#F44336用户当天未打卡补打卡#FFC107用户后续补打卡特殊日#9C27B0节假日或特殊活动日在Vue的data中初始化打卡数据data() { return { clockInData: [ { date: 2023-06-15, info: 已打卡, dropColor: #4CAF50, fontColor: #4CAF50 }, { date: 2023-06-16, info: 未打卡, dropColor: #F44336, fontColor: #F44336 }, { date: 2023-06-17, info: 补打卡, dropColor: #FFC107, fontColor: #FFC107, data: { remark: 出差补打卡 } } ] } }然后我们需要一个工具函数来处理日期格式确保与组件要求的格式一致methods: { formatDate(date) { const d new Date(date) const year d.getFullYear() const month (d.getMonth() 1).toString().padStart(2, 0) const day d.getDate().toString().padStart(2, 0) return ${year}-${month}-${day} }, // 模拟从服务器获取打卡数据 async fetchClockInData() { // 这里应该是实际的API调用 const response await this.$http.get(/api/clock-in) this.clockInData response.data.map(item ({ date: this.formatDate(item.date), info: item.status 1 ? 已打卡 : 未打卡, dropColor: item.status 1 ? #4CAF50 : #F44336, fontColor: item.status 1 ? #4CAF50 : #F44336, data: { remark: item.remark } })) } }4. 动态交互与业务逻辑一个完整的打卡系统不仅需要展示功能还需要处理用户的交互操作。让我们实现点击日期打卡的功能zjy-calendar :selectedclockInData dateClickhandleDateClick /对应的处理方法methods: { async handleDateClick({ detail }) { const clickedDate detail.date const today this.formatDate(new Date()) if (clickedDate today) { uni.showToast({ title: 不能预打卡未来日期, icon: none }) return } // 检查是否已打卡 const hasClocked this.clockInData.some( item item.date clickedDate item.info 已打卡 ) if (hasClocked) { uni.showToast({ title: 今天已经打卡过了, icon: none }) return } try { // 调用打卡API await this.$http.post(/api/clock-in, { date: clickedDate }) // 更新本地数据 this.clockInData [ ...this.clockInData.filter(item item.date ! clickedDate), { date: clickedDate, info: 已打卡, dropColor: #4CAF50, fontColor: #4CAF50 } ] uni.showToast({ title: 打卡成功 }) } catch (error) { uni.showToast({ title: 打卡失败, icon: none }) } } }5. 高级功能与样式定制zjy-calendar提供了丰富的自定义选项让我们进一步提升日历的视觉效果和用户体验。5.1 自定义日历头部可以通过插槽自定义日历头部zjy-calendar template v-slot:header view classcustom-header text我的打卡日历/text view classlegend view classlegend-item view classdot stylebackground-color: #4CAF50/view text已打卡/text /view view classlegend-item view classdot stylebackground-color: #F44336/view text未打卡/text /view /view /view /template /zjy-calendar style .custom-header { display: flex; flex-direction: column; padding: 20rpx; background-color: #f8f8f8; } .legend { display: flex; margin-top: 10rpx; } .legend-item { display: flex; align-items: center; margin-right: 30rpx; } .dot { width: 20rpx; height: 20rpx; border-radius: 50%; margin-right: 10rpx; } /style5.2 月份切换数据加载对于大量打卡数据我们需要实现分月加载methods: { async handleMonthSwitch({ detail }) { const { year, month } detail try { const response await this.$http.get(/api/clock-in/month, { params: { year, month } }) this.clockInData [ ...this.clockInData.filter(item { const [y, m] item.date.split(-) return !(y year m month) }), ...response.data.map(item ({ date: item.date, info: item.status 1 ? 已打卡 : 未打卡, dropColor: item.status 1 ? #4CAF50 : #F44336, fontColor: item.status 1 ? #4CAF50 : #F44336 })) ] } catch (error) { console.error(加载月份数据失败:, error) } } }5.3 性能优化技巧当打卡数据量很大时可以考虑以下优化措施虚拟滚动对于跨年度的打卡数据实现虚拟滚动只渲染可视区域内的日期数据分片按月分片加载数据减少单次渲染的数据量本地缓存使用uni.setStorage缓存已加载的数据减少网络请求// 在onLoad中尝试读取缓存 async loadCachedData() { try { const cached uni.getStorageSync(clockInData) if (cached) { this.clockInData JSON.parse(cached) } } catch (e) { console.log(读取缓存失败, e) } // 无论是否有缓存都请求最新数据 await this.fetchClockInData() } // 在数据更新时写入缓存 updateCache() { uni.setStorage({ key: clockInData, data: JSON.stringify(this.clockInData) }) }6. 完整项目集成将打卡日历集成到实际项目中时还需要考虑以下方面6.1 用户认证集成确保打卡数据与用户身份关联async fetchClockInData() { const userId uni.getStorageSync(userId) if (!userId) { uni.redirectTo({ url: /pages/login/login }) return } const response await this.$http.get(/api/clock-in, { params: { userId } }) // 处理响应数据 }6.2 多端适配uniapp的优势在于一次开发多端运行但各平台仍有差异需要注意微信小程序注意日历组件的层级问题避免被其他组件覆盖H5考虑PC端的鼠标交互体验App利用原生渲染优势实现更流畅的动画效果6.3 数据统计与可视化除了基础打卡功能还可以增加统计面板view classstats-panel view classstat-item text classstat-value{{ continuousDays }}/text text classstat-label连续打卡/text /view view classstat-item text classstat-value{{ totalDays }}/text text classstat-label累计打卡/text /view view classstat-item text classstat-value{{ successRate }}%/text text classstat-label打卡成功率/text /view /view对应的计算属性computed: { continuousDays() { // 实现连续打卡天数计算逻辑 let count 0 const today new Date() while (this.hasClockedOnDate(this.formatDate(new Date(today.setDate(today.getDate() - count))))) { count } return count }, totalDays() { return this.clockInData.filter(item item.info 已打卡).length }, successRate() { const total this.clockInData.length return total 0 ? Math.round((this.totalDays / total) * 100) : 0 } }