作为 人才小程序开发者, 我想要 实现考勤记录页面功能, 以便 人才用户能够查看自己的考勤日历、打卡明细和考勤统计数据。
现有系统状态:
@d8d/rencai-attendance-ui包基础框架已就绪src/api/index.ts)pages/attendance/index在TabBar中)原型设计参考:
docs/小程序原型/rencai.html 提供了考勤记录页面的完整原型设计技术集成模式:
yongren-attendance-ui的实现模式(如果存在)@d8d/rencai-attendance-ui包依赖API (史诗015):
依赖故事完成状态:
@d8d/rencai-attendance-ui中实现AttendancePage页面组件 (src/pages/AttendancePage/AttendancePage.tsx)src/components/AttendanceCalendar.tsx)
src/utils/mockAttendanceData.ts)
src/components/MonthSelector.tsx)
src/components/AttendanceStats.tsx)
src/types/attendance.ts)
src/components/AttendanceDetails.tsx)
src/components/AttendanceRecordItem.tsx)
src/types/attendance.ts)
import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'leftIcon="" leftText="" onClickLeft={() => {}}placeholder属性占位,移除手动空白占位fixed=true)mini-talent/src/pages/attendance/index.tsx:
@d8d/rencai-attendance-ui/pages/AttendancePage/AttendancePage导入AttendancePage组件tests/pages/AttendancePage/AttendancePage.test.tsx)
pnpm typecheck确保类型检查通过故事017.001完成状态:
src/api/index.ts)jest.config.cjs)故事017.002完成状态:
故事017.003完成状态:
maskUtils.ts)@d8d/mini-testing-utils提供的Taro mock故事017.012完成状态:
关键实现经验:
@d8d/server@d8d/mini-shared-ui-components中的实现来源: architecture/tech-stack.md
运行时和框架:
测试框架:
重要: mini项目使用Jest测试框架,与web应用使用的Vitest不同。
来源:
关键规范要求:
重要: UI包内部导入必须使用相对路径,不要使用别名。
正确示例:
// ✅ 正确: 使用相对路径导入同一包内的模块
import { mockAttendanceData } from '../../utils/mockAttendanceData'
import { AttendanceCalendar } from '../components/AttendanceCalendar'
错误示例:
// ❌ 错误: 不要使用别名导入UI包内部的模块
import { mockAttendanceData } from '@/utils/mockAttendanceData'
import { AttendanceCalendar } from '@/components/AttendanceCalendar'
{
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"import": "./dist/src/index.js",
"require": "./dist/src/index.js"
},
"./api": {
"types": "./src/api/index.ts",
"import": "./src/api/index.ts",
"require": "./src/api/index.ts"
},
"./pages/AttendancePage/AttendancePage": {
"types": "./dist/src/pages/AttendancePage/AttendancePage.d.ts",
"import": "./dist/src/pages/AttendancePage/AttendancePage.js",
"require": "./dist/src/pages/AttendancePage/AttendancePage.js"
}
}
}
重要: 本故事使用前端模拟数据,数据结构必须符合后续API接口规范。
模拟数据文件位置: src/utils/mockAttendanceData.ts
数据结构示例:
// 考勤统计数据(符合后续API接口规范)
export interface AttendanceStats {
attendanceRate: number // 出勤率(如:100)
normalDays: number // 正常出勤天数
lateCount: number // 迟到次数
earlyLeaveCount: number // 早退次数
absentCount: number // 缺勤次数
}
// 打卡记录数据(符合后续API接口规范)
export interface AttendanceRecord {
date: string // 日期(如:2023-11-25)
weekday: string // 星期(如:星期六)
checkInTime: string // 上班打卡时间(如:08:30)
checkOutTime: string // 下班打卡时间(如:17:30,未打卡为--:--)
status: AttendanceStatus // 打卡状态
}
export enum AttendanceStatus {
NORMAL = 'normal', // 正常
LATE = 'late', // 迟到
EARLY_LEAVE = 'early', // 早退
ABSENT = 'absent' // 缺勤
}
// 模拟数据导出
export const mockAttendanceStats: AttendanceStats = {
attendanceRate: 100,
normalDays: 28,
lateCount: 0,
earlyLeaveCount: 0,
absentCount: 0
}
export const mockAttendanceRecords: AttendanceRecord[] = [
{
date: '2023-11-25',
weekday: '星期六',
checkInTime: '08:30',
checkOutTime: '--:--',
status: AttendanceStatus.NORMAL
},
// ... 更多记录
]
后续API集成预留:
每个UI包必须创建jest.config.cjs配置文件,参照rencai-personal-info-ui/jest.config.cjs:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['@d8d/mini-testing-utils/setup'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^~/(.*)$': '<rootDir>/tests/$1',
'^@tarojs/taro$': '@d8d/mini-testing-utils/testing/taro-api-mock.ts',
'\\.(css|less|scss|sass)$': '@d8d/mini-testing-utils/testing/style-mock.js',
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'@d8d/mini-testing-utils/testing/file-mock.js'
},
testMatch: [
'<rootDir>/tests/**/*.spec.{ts,tsx}',
'<rootDir>/tests/**/*.test.{ts,tsx}'
],
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/**/index.{ts,tsx}',
'!src/**/*.stories.{ts,tsx}'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
testPathIgnorePatterns: [
'/node_modules/',
'/dist/',
'/coverage/'
],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
'^.+\\.(js|jsx)$': 'babel-jest'
},
transformIgnorePatterns: [
'/node_modules/(?!(swiper|@tarojs)/)'
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json']
}
关键配置说明:
setupFilesAfterEnv: 使用@d8d/mini-testing-utils/setup进行测试环境初始化moduleNameMapper:
^@/(.*)$ 和 ^~/(.*)$: 仅用于测试文件的路径映射,不在源代码中使用^@tarojs/taro$: 映射Taro API到mock@/或~/别名,只使用相对路径testMatch: 支持.spec.{ts,tsx}和.test.{ts,tsx}两种测试文件格式考勤记录页 (原型行303-481):
<!-- 月份选择 -->
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-700">考勤记录</h3>
<div class="flex items-center bg-gray-100 rounded-lg px-3 py-1">
<i class="fas fa-chevron-left text-gray-500 mr-2"></i>
<span class="text-sm text-gray-700 mr-2">2023年11月</span>
<i class="fas fa-chevron-right text-gray-500"></i>
</div>
</div>
移动端设计规范:
bg-gray-100chevron-left-20-solid、chevron-right-20-solidtext-sm)<!-- 考勤统计 -->
<div class="card bg-white p-4 mb-4">
<div class="flex justify-between items-center mb-4">
<div>
<p class="text-gray-700">出勤率</p>
<p class="text-2xl font-bold text-gray-800">100%</p>
</div>
<div class="text-right">
<p class="text-gray-700">正常出勤</p>
<p class="text-2xl font-bold text-gray-800">28天</p>
</div>
</div>
<div class="flex justify-between text-sm text-gray-500">
<span>迟到: 0次</span>
<span>早退: 0次</span>
<span>缺勤: 0次</span>
</div>
</div>
移动端设计规范:
bg-whitep-4)mb-4)text-2xl)text-sm)<!-- 日历视图 -->
<div class="card bg-white p-4 mb-4">
<h3 class="font-semibold text-gray-700 mb-3">考勤日历</h3>
<div class="grid grid-cols-7 gap-1 mb-2">
<div class="text-center text-xs text-gray-500">日</div>
<div class="text-center text-xs text-gray-500">一</div>
<div class="text-center text-xs text-gray-500">二</div>
<div class="text-center text-xs text-gray-500">三</div>
<div class="text-center text-xs text-gray-500">四</div>
<div class="text-center text-xs text-gray-500">五</div>
<div class="text-center text-xs text-gray-500">六</div>
</div>
<div class="grid grid-cols-7 gap-1">
<!-- 日期单元格 -->
<div class="calendar-day checked">1</div>
<div class="calendar-day checked">2</div>
<!-- ... 更多日期 -->
</div>
</div>
移动端设计规范:
grid grid-cols-7gap-1)text-xs)rounded-fullbg-green-500)bg-gray-200)<!-- 打卡明细 -->
<div class="card bg-white p-4">
<h3 class="font-semibold text-gray-700 mb-3">打卡明细</h3>
<div class="space-y-3">
<!-- 打卡记录1 -->
<div class="flex justify-between items-center p-3 border border-gray-100 rounded-lg">
<div>
<p class="text-sm font-medium text-gray-800">11月25日 星期六</p>
<div class="flex text-xs text-gray-500 mt-1">
<span class="mr-3">上班: 08:30</span>
<span>下班: --:--</span>
</div>
</div>
<span class="bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full">正常</span>
</div>
<!-- ... 更多记录 -->
</div>
</div>
移动端设计规范:
bg-whiterounded-lg)space-y-3)text-sm)text-xs)rounded-full)bg-green-100 text-green-800)bg-yellow-100 text-yellow-800)bg-orange-100 text-orange-800)bg-red-100 text-red-800)整体移动端设计规范:
来源: docs/stories/017.012.story.md
TabBar页面规范(考勤记录页属于此类):
leftIcon=""和leftText=""隐藏返回按钮@d8d/mini-shared-ui-components/components/navbarNavbar集成示例:
import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
import { View, ScrollView } from '@tarojs/components'
export function AttendancePage() {
return (
<View className="h-screen bg-gray-100">
{/* Navbar导航栏 - TabBar页面无返回按钮 */}
<Navbar
title="考勤记录"
leftIcon=""
leftText=""
onClickLeft={() => {}}
placeholder
fixed
/>
{/* 页面内容 */}
<ScrollView scrollY className="h-full">
{/* 页面内容 */}
</ScrollView>
</View>
)
}
关键配置:
leftIcon="": 隐藏返回按钮图标leftText="": 隐藏返回按钮文字onClickLeft={() => {}}: 空函数(TabBar页面不需要返回功能)placeholder: 添加占位空间,避免内容被Navbar遮挡fixed: 固定在顶部来源: architecture/source-tree.md
mini-talent项目结构:
mini-talent/ # 人才小程序项目
├── src/
│ ├── app.tsx # 小程序入口
│ ├── app.config.ts # 小程序配置 (已在故事017.001中更新)
│ ├── app.css # 全局样式
│ ├── pages/ # 页面目录
│ │ ├── login/ # 登录页 (从UI包导入)
│ │ │ └── index.tsx
│ │ ├── index/ # 首页/个人主页 (从UI包导入)
│ │ │ └── index.tsx
│ │ ├── attendance/ # 考勤记录页 (从UI包导入) - 本故事
│ │ │ └── index.tsx
│ │ ├── personal-info/ # 个人信息页 (从UI包导入)
│ │ │ └── index.tsx
│ │ └── settings/ # 设置页 (从UI包导入)
│ │ └── index.tsx
├── package.json
├── jest.config.js # Jest配置
└── tsconfig.json
mini-ui-packages目录结构:
mini-ui-packages/
├── rencai-attendance-ui/ # 人才考勤记录UI包
│ ├── src/
│ │ ├── api/
│ │ │ ├── attendanceClient.ts
│ │ │ └── index.ts
│ │ ├── pages/
│ │ │ └── AttendancePage/
│ │ │ ├── AttendancePage.tsx
│ │ │ └── index.ts (可选)
│ │ ├── components/ # UI组件
│ │ │ ├── AttendanceCalendar.tsx # 考勤日历组件
│ │ │ ├── AttendanceStats.tsx # 考勤统计卡片
│ │ │ ├── MonthSelector.tsx # 月份选择器
│ │ │ ├── AttendanceDetails.tsx # 打卡明细列表
│ │ │ └── AttendanceRecordItem.tsx # 打卡记录项
│ │ ├── types/
│ │ │ └── attendance.ts # 类型定义
│ │ ├── utils/
│ │ │ └── mockAttendanceData.ts # 前端模拟数据
│ │ └── index.ts
│ ├── package.json # 包含exports配置
│ ├── jest.config.cjs
│ └── tsconfig.json
└── mini-shared-ui-components/ # 通用小程序UI组件
├── src/
│ └── components/
│ ├── status-bar.tsx
│ ├── page-container.tsx
│ ├── navbar.tsx
│ └── tab-bar.tsx
└── ...
mini-talent页面导入方式:
// mini-talent/src/pages/attendance/index.tsx
import AttendancePage from '@d8d/rencai-attendance-ui/pages/AttendancePage/AttendancePage'
import { AuthContextProvider, useAuth } from '@d8d/rencai-auth-ui/utils'
function Attendance() {
const { isLoggedIn } = useAuth()
// 未登录跳转到登录页
if (!isLoggedIn) {
Taro.navigateTo({ url: '/pages/login/index' })
return null
}
return <AttendancePage />
}
export default function AttendanceIndex() {
return (
<AuthContextProvider>
<Attendance />
</AuthContextProvider>
)
}
重要: 在Taro小程序中,<View> 组件内的子元素默认是横向布局(flex-row),需要显式添加 flex flex-col 类才能实现垂直布局。
正确示例:
// ✅ 正确: 使用 flex flex-col 实现垂直布局
<View className="flex flex-col">
<Text>姓名: 张三</Text>
<Text>性别: 男</Text>
<Text>年龄: 35</Text>
</View>
// ❌ 错误: 缺少 flex flex-col,子元素会横向排列
<View>
<Text>姓名: 张三</Text>
<Text>性别: 男</Text>
<Text>年龄: 35</Text>
</View>
考勤统计卡片示例:
import { View, Text } from '@tarojs/components'
export function AttendanceStats({ stats }: { stats: AttendanceStats }) {
return (
<View className="bg-white rounded-lg p-4 mb-4">
{/* 两列统计布局 - 水平排列 */}
<View className="flex justify-between items-center mb-4">
<View>
<Text className="text-gray-700">出勤率</Text>
<Text className="text-2xl font-bold text-gray-800">{stats.attendanceRate}%</Text>
</View>
<View className="text-right">
<Text className="text-gray-700">正常出勤</Text>
<Text className="text-2xl font-bold text-gray-800">{stats.normalDays}天</Text>
</View>
</View>
{/* 异常统计 - 水平排列 */}
<View className="flex justify-between text-sm text-gray-500">
<Text>迟到: {stats.lateCount}次</Text>
<Text>早退: {stats.earlyLeaveCount}次</Text>
<Text>缺勤: {stats.absentCount}次</Text>
</View>
</View>
)
}
关键点:
flex justify-between)显示两列统计flex justify-between)显示三项异常flex flex-col 实现垂直布局flex flex-col来源: architecture/mini-ui-package-standards.md
重要: 不要使用emoji,必须使用Heroicons图标类。
图标类命名格式: i-heroicons-{图标名称}-{尺寸}-{样式}
本故事需要的图标:
chevron-left-20-solid - 左箭头(上个月)chevron-right-20-solid - 右箭头(下个月)calendar-20-solid - 日历图标正确示例:
// ✅ 正确: 使用Heroicons图标类
<View className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-500" />
<View className="i-heroicons-chevron-right-20-solid w-5 h-5 text-gray-500" />
// ❌ 错误: 使用emoji
<Text>←</Text>
<Text>→</Text>
月份选择器图标示例:
import { View, Text } from '@tarojs/components'
export function MonthSelector({ currentMonth, onPreviousMonth, onNextMonth }: MonthSelectorProps) {
return (
<View className="flex justify-between items-center mb-4">
<Text className="font-semibold text-gray-700">考勤记录</Text>
<View className="flex items-center bg-gray-100 rounded-lg px-3 py-1">
{/* 左箭头图标 */}
<View
className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-500 mr-2"
onClick={onPreviousMonth}
/>
<Text className="text-sm text-gray-700 mr-2">{currentMonth}</Text>
{/* 右箭头图标 */}
<View
className="i-heroicons-chevron-right-20-solid w-5 h-5 text-gray-500"
onClick={onNextMonth}
/>
</View>
</View>
)
}
日历网格布局:
import { View, Text } from '@tarojs/components'
export function AttendanceCalendar({ attendanceRecords }: AttendanceCalendarProps) {
// 计算当前月份的日期网格
const dates = getCalendarDates(currentYear, currentMonth)
return (
<View className="bg-white rounded-lg p-4 mb-4">
<Text className="font-semibold text-gray-700 mb-3">考勤日历</Text>
{/* 星期标题 */}
<View className="grid grid-cols-7 gap-1 mb-2">
<View className="text-center text-xs text-gray-500"><Text>日</Text></View>
<View className="text-center text-xs text-gray-500"><Text>一</Text></View>
<View className="text-center text-xs text-gray-500"><Text>二</Text></View>
<View className="text-center text-xs text-gray-500"><Text>三</Text></View>
<View className="text-center text-xs text-gray-500"><Text>四</Text></View>
<View className="text-center text-xs text-gray-500"><Text>五</Text></View>
<View className="text-center text-xs text-gray-500"><Text>六</Text></View>
</View>
{/* 日期网格 */}
<View className="grid grid-cols-7 gap-1">
{dates.map((date) => {
const attendance = getAttendanceForDate(date, attendanceRecords)
return (
<View
key={date.toString()}
className={`
calendar-day
text-center
p-2
rounded-full
${attendance ? 'bg-green-500 text-white' : 'bg-gray-200 text-gray-700'}
${isWeekend(date) ? 'text-gray-400' : ''}
`}
>
<Text className="text-sm">{date.getDate()}</Text>
</View>
)
})}
</View>
</View>
)
}
关键点:
grid grid-cols-7实现7列网格布局gap-1设置网格间距rounded-full)bg-green-500)bg-gray-200)text-gray-400)列表组件结构:
import { View, Text } from '@tarojs/components'
export function AttendanceDetails({ records }: AttendanceDetailsProps) {
return (
<View className="bg-white rounded-lg p-4">
<Text className="font-semibold text-gray-700 mb-3">打卡明细</Text>
{/* 列表容器 - 垂直布局 */}
<View className="flex flex-col space-y-3">
{records.map((record) => (
<AttendanceRecordItem key={record.date} record={record} />
))}
</View>
</View>
)
}
export function AttendanceRecordItem({ record }: AttendanceRecordItemProps) {
const statusLabel = getStatusLabel(record.status)
const statusColor = getStatusColor(record.status)
return (
<View className="flex justify-between items-center p-3 border border-gray-100 rounded-lg">
{/* 左侧:日期和时间 */}
<View>
<Text className="text-sm font-medium text-gray-800">{record.date} {record.weekday}</Text>
<View className="flex text-xs text-gray-500 mt-1">
<Text className="mr-3">上班: {record.checkInTime}</Text>
<Text>下班: {record.checkOutTime}</Text>
</View>
</View>
{/* 右侧:状态标签 */}
<View className={`text-xs px-2 py-1 rounded-full ${statusColor}`}>
<Text>{statusLabel}</Text>
</View>
</View>
)
}
状态颜色映射:
function getStatusColor(status: AttendanceStatus): string {
switch (status) {
case AttendanceStatus.NORMAL:
return 'bg-green-100 text-green-800'
case AttendanceStatus.LATE:
return 'bg-yellow-100 text-yellow-800'
case AttendanceStatus.EARLY_LEAVE:
return 'bg-orange-100 text-orange-800'
case AttendanceStatus.ABSENT:
return 'bg-red-100 text-red-800'
default:
return 'bg-gray-100 text-gray-800'
}
}
function getStatusLabel(status: AttendanceStatus): string {
switch (status) {
case AttendanceStatus.NORMAL:
return '正常'
case AttendanceStatus.LATE:
return '迟到'
case AttendanceStatus.EARLY_LEAVE:
return '早退'
case AttendanceStatus.ABSENT:
return '缺勤'
default:
return '未知'
}
}
数据工厂模式:
// src/utils/mockAttendanceData.ts
/**
* 生成指定月份的模拟考勤数据
* @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[] = dates.map((date) => {
const weekday = getWeekday(date)
const isWeekend = weekday === '星期六' || weekday === '星期日'
// 周末不打卡
if (isWeekend) {
return {
date: formatDate(date),
weekday,
checkInTime: '--:--',
checkOutTime: '--:--',
status: AttendanceStatus.NORMAL
}
}
// 工作日随机生成打卡记录
const random = Math.random()
let status = AttendanceStatus.NORMAL
let checkInTime = '08:30'
let checkOutTime = '17:30'
if (random < 0.1) {
status = AttendanceStatus.LATE
checkInTime = '09:15'
} else if (random < 0.2) {
status = AttendanceStatus.EARLY_LEAVE
checkOutTime = '16:30'
}
return {
date: formatDate(date),
weekday,
checkInTime,
checkOutTime,
status
}
})
// 计算统计数据
const stats = calculateAttendanceStats(records)
return { stats, records }
}
/**
* 计算考勤统计数据
*/
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
const attendanceRate = workDays.length > 0
? Math.round((normalDays / workDays.length) * 100)
: 0
return {
attendanceRate,
normalDays,
lateCount,
earlyLeaveCount,
absentCount
}
}
来源: architecture/testing-strategy.md
测试框架:
测试文件位置:
mini-ui-packages/<package-name>/
└── tests/
├── unit/ # 单元测试
│ └── components/
│ ├── AttendanceCalendar.test.tsx
│ ├── AttendanceStats.test.tsx
│ ├── AttendanceDetails.test.tsx
│ ├── AttendanceRecordItem.test.tsx
│ └── MonthSelector.test.tsx
└── pages/ # 页面组件测试
└── AttendancePage/
└── AttendancePage.test.tsx
测试要求:
pnpm typecheck确保类型检查通过Mock响应示例:
const mockAttendanceStats: AttendanceStats = {
attendanceRate: 100,
normalDays: 28,
lateCount: 0,
earlyLeaveCount: 0,
absentCount: 0
}
const mockAttendanceRecords: AttendanceRecord[] = [
{
date: '2023-11-25',
weekday: '星期六',
checkInTime: '08:30',
checkOutTime: '--:--',
status: AttendanceStatus.NORMAL
},
// ... 更多记录
]
来源: architecture/coding-standards.md
关键编码规范:
开发Mini UI包时,必须参考并遵循Mini UI包开发规范,该规范基于史诗011和017的经验总结。
flex flex-col实现垂直布局flex flex-col 实现垂直布局w-5 h-5、text-lg等)@/、~/等),必须使用相对路径路径使用示例:
// ✅ 正确: UI包内部使用相对路径
import { mockAttendanceData } from '../../utils/mockAttendanceData'
import { AttendanceCalendar } from '../components/AttendanceCalendar'
// ✅ 正确: 跨包导入使用workspace包名
import { SharedComponent } from '@d8d/mini-shared-ui-components'
// ❌ 错误: UI包内部使用别名
import { mockAttendanceData } from '@/utils/mockAttendanceData'
import { AttendanceCalendar } from '@/components/AttendanceCalendar'
mini-ui-packages/yongren-attendance-ui (如果存在)mini-ui-packages/yongren-dashboard-ui
src/pages/Dashboard/Dashboard.tsxjest.config.cjsmini-ui-packages/rencai-personal-info-ui
主要风险:
缓解措施:
来源: architecture/testing-strategy.md
测试框架:
测试文件位置:
mini-ui-packages/<package-name>/
└── tests/
├── unit/ # 单元测试
│ └── components/
│ ├── AttendanceCalendar.test.tsx
│ ├── AttendanceStats.test.tsx
│ ├── AttendanceDetails.test.tsx
│ ├── AttendanceRecordItem.test.tsx
│ └── MonthSelector.test.tsx
└── pages/ # 页面组件测试
└── AttendancePage/
└── AttendancePage.test.tsx
组件测试:
日历组件测试:
统计组件测试:
打卡明细测试:
集成测试:
回归测试:
pnpm typecheck确保类型检查通过# 运行所有测试
cd mini-ui-packages/rencai-attendance-ui && pnpm test
# 运行特定测试
pnpm test --testNamePattern="AttendancePage"
# 生成覆盖率报告
pnpm test:coverage
| 日期 | 版本 | 描述 | 作者 |
|---|---|---|---|
| 2025-12-26 | 1.0 | 创建故事文档 | Bob (Scrum Master) |
此部分由开发代理在实施过程中填写
Claude Sonnet (claude-sonnet-4-20250514)
无特殊调试需求
src/types/attendance.ts): 创建了AttendanceStats、AttendanceRecord接口和AttendanceStatus枚举src/utils/mockAttendanceData.ts): 实现数据工厂模式,生成符合后续API接口规范的模拟数据src/components/MonthSelector.tsx): 支持月份切换,使用Heroicons图标src/components/AttendanceStats.tsx): 显示出勤率、正常出勤天数和异常统计src/components/AttendanceCalendar.tsx): 7列网格布局,标记已打卡日期(绿色)和未打卡日期(灰色)src/components/AttendanceRecordItem.tsx): 显示单条打卡记录,支持状态颜色标签src/components/AttendanceDetails.tsx): 显示打卡记录列表,按日期倒序排列src/pages/AttendancePage/AttendancePage.tsx): 集成所有子组件,实现月份切换和数据加载新增源代码文件:
mini-ui-packages/rencai-attendance-ui/src/types/attendance.ts - 类型定义mini-ui-packages/rencai-attendance-ui/src/utils/mockAttendanceData.ts - 前端模拟数据mini-ui-packages/rencai-attendance-ui/src/components/MonthSelector.tsx - 月份选择器mini-ui-packages/rencai-attendance-ui/src/components/AttendanceStats.tsx - 考勤统计卡片mini-ui-packages/rencai-attendance-ui/src/components/AttendanceCalendar.tsx - 考勤日历mini-ui-packages/rencai-attendance-ui/src/components/AttendanceRecordItem.tsx - 打卡记录项mini-ui-packages/rencai-attendance-ui/src/components/AttendanceDetails.tsx - 打卡明细列表修改源代码文件:
mini-ui-packages/rencai-attendance-ui/src/pages/AttendancePage/AttendancePage.tsx - 更新主页面组件新增测试文件:
tests/unit/components/MonthSelector.test.tsx - 月份选择器测试tests/unit/components/AttendanceStats.test.tsx - 考勤统计测试tests/unit/components/AttendanceCalendar.test.tsx - 考勤日历测试tests/unit/components/AttendanceRecordItem.test.tsx - 打卡记录项测试tests/unit/components/AttendanceDetails.test.tsx - 打卡明细列表测试tests/pages/AttendancePage/AttendancePage.test.tsx - 主页面测试测试结果: 36个测试全部通过
此部分由QA代理在审查完成后填写