文章目录
Work-calendar 工作日历组件
基于
uni-calendar进行深度定制开发,专为企业级工作管理场景设计,支持周计划、日报状态展示与提报、农历显示、休假标识及多事件封装,适用于微信小程序等多端应用。
1. 功能亮点
- ✅ 支持 周计划/日报状态展示(可配置)
- ✅ 农历显示、快速回到今日、月份切换
- ✅ 封装日历点击、周计划点击、日报点击等交互事件
- ✅ 支持 休假状态标识(每日独立配置)
- ✅ 可配置是否需要审核流程(
needState) - ✅ 兼容 Vue3
<script setup>语法,结构清晰易维护
2. 效果预览


图:日历清晰标注周计划状态(如审核中、已提交)、日报填写情况及休假标识,提升用户操作效率。
3. 插件市场链接
本组件已在 UniApp 插件市场发布,欢迎下载使用:
👉 Work-calendar - 工作日历组件(Vue3 版本)
4. 使用说明
1. 安装与引入
将插件下载至项目 components 目录,并在页面中引入:
import Calendar from '@/components/calendar-zhikuany/calendar.vue'
2. 模板使用
在 .vue 文件中引入并配置组件:
<template>
<view>
<calendar
ref="calendar"
:date="date"
@monthSwitch="monthSwitch"
@change="handleSelect"
@weekPlanClick="weekPlanClick"
@datePlanClick="datePlanClick"
lunar
:showDay="prop.showDay"
:showPlan="prop.showPlan"
:showMonth="prop.showMonth"
:needState="prop.needState"
/>
</view>
</template>
属性说明(Props)
| 参数 | 类型 | 说明 |
|---|---|---|
date | String | 当前选中日期,默认为今天 |
lunar | Boolean | 是否显示农历 |
showDay | Boolean | 是否展示日报状态 |
showPlan | Boolean | 是否展示周计划状态 |
showMonth | Boolean | 是否以月份为背景展示 |
needState | Boolean | 周计划/日报是否需要审核状态控制 |
startDate / endDate | String | 可选日期范围 |
range | Boolean | 是否开启范围选择 |
事件说明(Events)
| 事件 | 回调参数 | 说明 |
|---|---|---|
@change | { year, month, date, extraInfo } | 日期改变时触发 |
@monthSwitch | { year, month } | 切换月份时触发 |
@weekPlanClick | weekData | 点击周计划区域时触发 |
@datePlanClick | dayData | 点击某一日时触发 |
3. 初始化数据
通过 setPlanList 方法动态设置周计划与日报数据:
const setDate = (year: number, month: number) => {
uni.showLoading({ title: '' })
// 模拟接口延迟
setTimeout(() => {
uni.hideLoading()
date.value = '2025-09-12' // 默认选中日期
calendar.value.setPlanList(planList) // 设置周计划数据
}, 1000)
}
💡 实际项目中,
planList应从后端接口获取,包含用户、计划周期、每日状态等信息。
4. 核心逻辑解析
组件内部通过 _getWeek 方法对日历数据进行结构化处理,核心逻辑如下:
_getWeek(dateData) {
const { year, month } = this.getDate(dateData)
const firstDay = new Date(year, month - 1, 1).getDay()
const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1 // 周一为第一天
const currentDay = new Date(year, month, 0).getDate()
// 构建完整日历数组(包含上月尾、本月、下月头)
let canlender = []
canlender = canlender.concat(
this._getLastMonthDays(adjustedFirstDay),
this._currentMonthDys(currentDay),
this._getNextMonthDays(42 - canlender.length)
)
// 按周分组,填充周计划与日报状态
let weekTemp = {}
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
// 每周开始,查找对应周计划
const weekBeginDate = canlender[i].fullDate
const weekplan = this.planList.find(item => item.beginDate === weekBeginDate) || {}
weekTemp[parseInt(i / 7)] = {
weeks: new Array(7),
planCode: weekplan.planCode,
planId: weekplan.planId,
weekWorkPlan: weekplan.weekWorkPlan,
dataState: this.isEmpty(weekplan.dataState) ? weekplan.dataState : 1,
// ...其他字段
}
}
// 根据星期几填充日报状态和休假标识
const dayPlan = {}
switch (i % 7) {
case 0: // 周一
dayPlan.dayStatus = this.isEmptyWithZero(weekplan.mondayStatus) ? weekplan.mondayStatus : 2
dayPlan.isHoliday = weekplan.mondayIsHoliday
break
// ...其他天
}
weekTemp[parseInt(i / 7)].weeks[i % 7] = { ...canlender[i], ...dayPlan }
}
this.weeks = weekTemp
}
该逻辑实现了:按周聚合数据 + 状态映射 + 休假标识注入,确保日历展示准确。
5. 完整示例代码
<template>
<view>
<calendar
ref="calendar"
:date="date"
@monthSwitch="monthSwitch"
@change="handleSelect"
@weekPlanClick="weekPlanClick"
@datePlanClick="datePlanClick"
lunar
:showDay="prop.showDay"
:showPlan="prop.showPlan"
:showMonth="prop.showMonth"
:needState="prop.needState"
/>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import { planList } from './data.js'
import Calendar from '@/components/calendar-zhikuany/calendar.vue'
const calendar = ref<any>({})
const date = ref('')
const query = defineProps<{ prop: string }>()
const prop = ref<any>({})
// 页面加载:解析配置
onLoad(() => {
prop.value = JSON.parse(query.prop)
})
// 页面显示:初始化日历
onShow(() => {
initDate()
})
const initDate = () => {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
setDate(year, month)
}
// 设置日历数据(模拟接口调用)
const setDate = (year: number, month: number) => {
uni.showLoading({ title: '' })
setTimeout(() => {
uni.hideLoading()
date.value = '2025-09-12'
calendar.value.setPlanList(planList)
}, 1000)
}
// 日期选中事件
const handleSelect = (e: any) => {
console.log('选中日期:', e)
}
// 周计划点击
const weekPlanClick = (item: any) => {
if ([0, 1].includes(item.dataState)) {
uni.showToast({ title: '查看周计划详情', icon: 'none' })
} else {
if (!checkWeekRange(item.beginDate, item.endDate)) {
uni.showToast({ title: '仅可提交本周或下周计划', icon: 'none' })
return
}
uni.showToast({ title: '跳转周计划填报页', icon: 'none' })
}
}
// 检查是否为本周或下周
function checkWeekRange(beginDate: string, endDate: string): boolean {
const begin = new Date(beginDate)
const end = new Date(endDate)
const today = new Date()
today.setHours(0, 0, 0, 0)
const dayOfWeek = today.getDay()
const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek
const currentWeekStart = new Date(today)
currentWeekStart.setDate(today.getDate() + diffToMonday)
const currentWeekEnd = new Date(currentWeekStart)
currentWeekEnd.setDate(currentWeekStart.getDate() + 6)
const nextWeekStart = new Date(currentWeekStart)
nextWeekStart.setDate(currentWeekStart.getDate() + 7)
const nextWeekEnd = new Date(currentWeekEnd)
nextWeekEnd.setDate(currentWeekEnd.getDate() + 7)
return (
(begin >= currentWeekStart && end <= currentWeekEnd) || // 本周
(begin >= nextWeekStart && end <= nextWeekEnd) || // 下周
(begin <= currentWeekEnd && end >= nextWeekStart) // 跨周
)
}
// 日报点击
const datePlanClick = (item: any) => {
if (prop.value.showPlan) {
if (item.dataState == null) {
uni.showToast({ title: '请先提交周计划', icon: 'none' })
return
}
if (item.dataState === 0) {
uni.showToast({ title: '周计划审核中...', icon: 'none' })
return
}
}
if ([1, 2].includes(item.dayStatus)) {
uni.showToast({ title: '跳转日报详情页', icon: 'none' })
} else {
if (!isDateInCurrentWeek(item.fullDate)) {
uni.showToast({ title: '仅可提交本周日报', icon: 'none' })
return
}
uni.showToast({ title: '跳转日报填写页', icon: 'none' })
}
}
// 判断日期是否在本周内
function isDateInCurrentWeek(targetDate: string): boolean {
const date = new Date(targetDate)
const today = new Date()
const currentWeekMonday = new Date(today)
currentWeekMonday.setDate(today.getDate() - today.getDay() + 1)
currentWeekMonday.setHours(0, 0, 0, 0)
const currentWeekSunday = new Date(currentWeekMonday)
currentWeekSunday.setDate(currentWeekMonday.getDate() + 6)
return date >= currentWeekMonday && date <= currentWeekSunday
}
// 月份切换
const monthSwitch = (e: any) => {
console.log('切换月份:', e)
setDate(e.year, e.month)
}
</script>
6. 周计划/日报数据结构示例
const planList = [{
"planId": 58,
"planCode": "1015297758478274563",
"beginDate": "2025-09-08",
"endDate": "2025-09-14",
"planPeriod": "2025-09-08~2025-09-14",
"userCode": "1009761934881456147",
"username": "员工001",
"departCode": "951784785749401608",
"departName": "xxx总公司",
// 每日状态
"monday": "2025-09-08", "mondayStatus": 0, "mondayIsHoliday": null,
"tuesday": "2025-09-09", "tuesdayStatus": 1, "tuesdayIsHoliday": 1,
// ...周三至周日
"weekWorkPlan": "本周工作计划内容",
"workSummary": "工作总结",
"coordinateHelp": "需协调事项",
"attachIds": "",
"gmtCreate": "2025-09-03 08:27:25",
"dataState": 0, // 0:审核中, 1:已通过, 2:驳回
}]
字段说明:
dayStatus: 日报状态(0:未提交, 1:审核中, 2:已通过)isHoliday: 是否休假(1:是, 0或null:否)
7. 最佳实践建议
- 性能优化:对于大数据量,建议按月分页加载
planList。 - 类型安全:使用 TypeScript 定义
planList接口,提升代码健壮性。 - 国际化支持:可扩展农历、节日提示等本地化功能。
- 权限控制:结合用户角色控制“提报”按钮的可见性。
8. 总结
Work-calendar 是一款高度可定制的企业级工作日历组件,完美融合了 周计划管理 + 日报提报 + 休假标识 + 审核流程,极大提升了员工日常工作的可视化与便捷性。结合 UniApp 的跨平台能力,适用于各类 OA、ERP、考勤系统。
立即体验:点击前往插件市场下载
1万+

被折叠的 条评论
为什么被折叠?



