微信小程序 工作日历 周计划日报 修改等提报和状态展示功能,支持h5,Android ,ios,基于uniapp,适配vue2和vue3

Work-calendar 工作日历组件

基于 uni-calendar 进行深度定制开发,专为企业级工作管理场景设计,支持周计划、日报状态展示与提报、农历显示、休假标识及多事件封装,适用于微信小程序等多端应用。

1. 功能亮点

  • ✅ 支持 周计划/日报状态展示(可配置)
  • ✅ 农历显示、快速回到今日、月份切换
  • ✅ 封装日历点击、周计划点击、日报点击等交互事件
  • ✅ 支持 休假状态标识(每日独立配置)
  • ✅ 可配置是否需要审核流程(needState
  • ✅ 兼容 Vue3 <script setup> 语法,结构清晰易维护

2. 效果预览

工作日历效果图1

工作日历效果图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)
参数类型说明
dateString当前选中日期,默认为今天
lunarBoolean是否显示农历
showDayBoolean是否展示日报状态
showPlanBoolean是否展示周计划状态
showMonthBoolean是否以月份为背景展示
needStateBoolean周计划/日报是否需要审核状态控制
startDate / endDateString可选日期范围
rangeBoolean是否开启范围选择
事件说明(Events)
事件回调参数说明
@change{ year, month, date, extraInfo }日期改变时触发
@monthSwitch{ year, month }切换月份时触发
@weekPlanClickweekData点击周计划区域时触发
@datePlanClickdayData点击某一日时触发

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. 最佳实践建议

  1. 性能优化:对于大数据量,建议按月分页加载 planList
  2. 类型安全:使用 TypeScript 定义 planList 接口,提升代码健壮性。
  3. 国际化支持:可扩展农历、节日提示等本地化功能。
  4. 权限控制:结合用户角色控制“提报”按钮的可见性。

8. 总结

Work-calendar 是一款高度可定制的企业级工作日历组件,完美融合了 周计划管理 + 日报提报 + 休假标识 + 审核流程,极大提升了员工日常工作的可视化与便捷性。结合 UniApp 的跨平台能力,适用于各类 OA、ERP、考勤系统。

立即体验点击前往插件市场下载

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木易 士心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值