|
|
@@ -0,0 +1,177 @@
|
|
|
+/**
|
|
|
+ * 前端模拟考勤数据
|
|
|
+ * 数据结构符合后续API接口规范,便于后续替换为真实API
|
|
|
+ */
|
|
|
+
|
|
|
+import { AttendanceStats, AttendanceRecord, AttendanceStatus } from '../types/attendance'
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取指定月份的所有日期
|
|
|
+ */
|
|
|
+function getDatesInMonth(year: number, month: number): Date[] {
|
|
|
+ const dates: Date[] = []
|
|
|
+ const lastDay = new Date(year, month, 0).getDate()
|
|
|
+
|
|
|
+ for (let day = 1; day <= lastDay; day++) {
|
|
|
+ dates.push(new Date(year, month - 1, day))
|
|
|
+ }
|
|
|
+
|
|
|
+ return dates
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 格式化日期为 YYYY-MM-DD
|
|
|
+ */
|
|
|
+function formatDate(date: Date): string {
|
|
|
+ const year = date.getFullYear()
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
+ const day = String(date.getDate()).padStart(2, '0')
|
|
|
+ return `${year}-${month}-${day}`
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取星期名称
|
|
|
+ */
|
|
|
+function getWeekday(date: Date): string {
|
|
|
+ const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
|
|
+ return weekdays[date.getDay()]
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 判断是否为周末
|
|
|
+ */
|
|
|
+function isWeekend(date: Date): boolean {
|
|
|
+ const day = date.getDay()
|
|
|
+ return day === 0 || day === 6
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 计算考勤统计数据
|
|
|
+ */
|
|
|
+function calculateAttendanceStats(records: AttendanceRecord[]): AttendanceStats {
|
|
|
+ // 筛选出工作日(排除周末)
|
|
|
+ const workDays = records.filter(r => {
|
|
|
+ const weekday = r.weekday
|
|
|
+ return weekday !== '星期六' && weekday !== '星期日'
|
|
|
+ })
|
|
|
+
|
|
|
+ const normalDays = records.filter(r => r.status === AttendanceStatus.NORMAL).length
|
|
|
+ const lateCount = records.filter(r => r.status === AttendanceStatus.LATE).length
|
|
|
+ const earlyLeaveCount = records.filter(r => r.status === AttendanceStatus.EARLY_LEAVE).length
|
|
|
+ const absentCount = records.filter(r => r.status === AttendanceStatus.ABSENT).length
|
|
|
+
|
|
|
+ // 出勤率 = 正常出勤天数 / 工作日总数 * 100
|
|
|
+ const attendanceRate = workDays.length > 0
|
|
|
+ ? Math.round((normalDays / workDays.length) * 100)
|
|
|
+ : 0
|
|
|
+
|
|
|
+ return {
|
|
|
+ attendanceRate,
|
|
|
+ normalDays,
|
|
|
+ lateCount,
|
|
|
+ earlyLeaveCount,
|
|
|
+ absentCount
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 生成指定月份的模拟考勤数据
|
|
|
+ * @param year 年份
|
|
|
+ * @param month 月份 (1-12)
|
|
|
+ * @returns 考勤统计数据和打卡记录
|
|
|
+ */
|
|
|
+export function generateMockAttendanceData(year: number, month: number): {
|
|
|
+ stats: AttendanceStats
|
|
|
+ records: AttendanceRecord[]
|
|
|
+} {
|
|
|
+ const dates = getDatesInMonth(year, month)
|
|
|
+ const records: AttendanceRecord[] = []
|
|
|
+
|
|
|
+ // 从后向前生成记录(倒序)
|
|
|
+ for (let i = dates.length - 1; i >= 0; i--) {
|
|
|
+ const date = dates[i]
|
|
|
+ const weekday = getWeekday(date)
|
|
|
+ const weekend = isWeekend(date)
|
|
|
+
|
|
|
+ // 周末不打卡
|
|
|
+ if (weekend) {
|
|
|
+ records.push({
|
|
|
+ date: formatDate(date),
|
|
|
+ weekday,
|
|
|
+ checkInTime: '--:--',
|
|
|
+ checkOutTime: '--:--',
|
|
|
+ status: AttendanceStatus.NORMAL
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 工作日随机生成打卡记录
|
|
|
+ const random = Math.random()
|
|
|
+ let status = AttendanceStatus.NORMAL
|
|
|
+ let checkInTime = '08:30'
|
|
|
+ let checkOutTime = '17:30'
|
|
|
+
|
|
|
+ // 10%概率迟到
|
|
|
+ if (random < 0.1) {
|
|
|
+ status = AttendanceStatus.LATE
|
|
|
+ checkInTime = '09:15'
|
|
|
+ }
|
|
|
+ // 10%概率早退(排除迟到的情况)
|
|
|
+ else if (random >= 0.1 && random < 0.2) {
|
|
|
+ status = AttendanceStatus.EARLY_LEAVE
|
|
|
+ checkOutTime = '16:30'
|
|
|
+ }
|
|
|
+
|
|
|
+ records.push({
|
|
|
+ date: formatDate(date),
|
|
|
+ weekday,
|
|
|
+ checkInTime,
|
|
|
+ checkOutTime,
|
|
|
+ status
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const stats = calculateAttendanceStats(records)
|
|
|
+
|
|
|
+ return { stats, records }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 格式化月份显示
|
|
|
+ * @param year 年份
|
|
|
+ * @param month 月份 (1-12)
|
|
|
+ * @returns 格式化的月份字符串(如:2023年11月)
|
|
|
+ */
|
|
|
+export function formatMonth(year: number, month: number): string {
|
|
|
+ return `${year}年${month}月`
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取当前年月
|
|
|
+ */
|
|
|
+export function getCurrentYearMonth(): { year: number; month: number } {
|
|
|
+ const now = new Date()
|
|
|
+ return {
|
|
|
+ year: now.getFullYear(),
|
|
|
+ month: now.getMonth() + 1
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取上个月
|
|
|
+ */
|
|
|
+export function getPreviousMonth(year: number, month: number): { year: number; month: number } {
|
|
|
+ if (month === 1) {
|
|
|
+ return { year: year - 1, month: 12 }
|
|
|
+ }
|
|
|
+ return { year, month: month - 1 }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取下个月
|
|
|
+ */
|
|
|
+export function getNextMonth(year: number, month: number): { year: number; month: number } {
|
|
|
+ if (month === 12) {
|
|
|
+ return { year: year + 1, month: 1 }
|
|
|
+ }
|
|
|
+ return { year, month: month + 1 }
|
|
|
+}
|