Ver Fonte

docs(story): 创建故事017.004考勤记录功能实现

- 基于`@d8d/rencai-attendance-ui`包实现考勤记录页面
- 包含考勤日历视图、月份切换、考勤统计、打卡明细列表
- 使用前端模拟数据(史诗015考勤API为P2延期功能)
- 数据结构符合后续API接口规范,便于后续替换
- 严格遵循Mini UI包开发规范(Taro布局、Heroicons图标、Jest测试)
- TabBar页面使用Navbar无返回按钮
- 完整的原型设计参考和技术实现指导

🤖 Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname há 3 semanas atrás
pai
commit
2cd4dfcdd4
1 ficheiros alterados com 1231 adições e 0 exclusões
  1. 1231 0
      docs/stories/017.004.story.md

+ 1231 - 0
docs/stories/017.004.story.md

@@ -0,0 +1,1231 @@
+# 故事017.004: 考勤记录功能实现
+
+## 元信息
+- **史诗**: 017 - 人才小程序功能实现
+- **优先级**: P1 - 核心功能
+- **状态**: Approved
+- **创建日期**: 2025-12-26
+- **负责人**: 开发团队
+
+## 故事描述
+
+**作为** 人才小程序开发者,
+**我想要** 实现考勤记录页面功能,
+**以便** 人才用户能够查看自己的考勤日历、打卡明细和考勤统计数据。
+
+### 背景
+
+**现有系统状态:**
+- 故事017.001已完成rencai mini ui包基础框架搭建
+- 故事017.002已完成登录与首页实现
+- 故事017.003已完成个人信息功能实现
+- `@d8d/rencai-attendance-ui`包基础框架已就绪
+- API客户端文件已创建(`src/api/index.ts`)
+- mini-talent项目路由结构已配置完成(`pages/attendance/index`在TabBar中)
+
+**原型设计参考:**
+- `docs/小程序原型/rencai.html` 提供了考勤记录页面的完整原型设计
+- 考勤记录页 (原型行303-481): 月份选择、考勤统计、日历视图、打卡明细
+
+**技术集成模式:**
+- 参照`yongren-attendance-ui`的实现模式(如果存在)
+- 考勤记录页使用`@d8d/rencai-attendance-ui`包
+- API调用逻辑封装在页面组件内部
+- TabBar页面使用Navbar无返回按钮(参照故事017.012规范)
+
+**依赖API (史诗015):**
+- ⚠️ 考勤记录API为P2延期功能
+- ✅ 当前使用前端模拟数据实现
+- 📋 前端数据结构符合后续API接口规范,便于后续替换为真实API
+
+**依赖故事完成状态:**
+- ✅ 故事017.001: rencai mini ui包基础框架搭建完成
+- ✅ 故事017.002: 登录与首页实现完成
+- ✅ 故事017.003: 个人信息功能实现完成
+- ✅ 故事017.012: 统一Navbar导航栏组件规范完成
+
+## 验收标准
+
+### 考勤日历视图
+- [ ] 考勤日历视图功能完整,正确标记打卡状态(前端模拟数据)
+- [ ] 日历视图显示当前月份的完整日期网格(7列布局)
+- [ ] 已打卡日期使用绿色标记显示
+- [ ] 未打卡日期使用灰色显示
+- [ ] 周末日期使用不同颜色标识
+
+### 月份切换功能
+- [ ] 支持月份切换,展示对应月份的考勤数据
+- [ ] 左右箭头按钮可切换到上个月/下个月
+- [ ] 月份选择器显示当前年月(如:2023年11月)
+- [ ] 切换月份后更新日历视图和统计数据
+
+### 考勤统计展示
+- [ ] 考勤统计数据展示完整(出勤率、正常出勤天数、迟到次数、早退次数、缺勤次数)
+- [ ] 出勤率以百分比形式显示(如:100%)
+- [ ] 正常出勤天数以数字形式显示(如:28天)
+- [ ] 异常统计(迟到、早退、缺勤)以次数形式显示
+
+### 打卡明细列表
+- [ ] 打卡明细列表功能完整,支持按日期倒序排列
+- [ ] 显示每日打卡详情(上班打卡时间、下班打卡时间、打卡状态)
+- [ ] 打卡状态使用彩色标签显示(正常-绿色、迟到-黄色、早退-橙色、缺勤-红色)
+- [ ] 显示日期和星期信息(如:11月25日 星期六)
+
+### 页面设计与布局
+- [ ] 页面设计符合原型标准,移动端体验良好
+- [ ] 已集成Navbar导航栏组件(TabBar页面,无返回按钮)
+- [ ] TabBarLayout底部导航正常工作
+- [ ] 页面布局包含:月份选择器、考勤统计卡片、日历视图、打卡明细列表
+
+### 前端模拟数据规范
+- [ ] 前端模拟数据结构符合后续API接口规范
+- [ ] 考勤统计数据结构包含:attendanceRate、normalDays、lateCount、earlyLeaveCount、absentCount
+- [ ] 打卡明细数据结构包含:date、weekday、checkInTime、checkOutTime、status
+- [ ] 数据结构便于后续替换为真实API
+
+### 集成与兼容性
+- [ ] 与前置故事(017.001、017.002、017.003)无缝集成
+- [ ] 现有mini-talent项目功能不受影响
+
+## 任务列表
+
+### 任务1: 创建考勤记录页面组件 (AC: 考勤日历视图)
+- [ ] 1.1 在`@d8d/rencai-attendance-ui`中实现`AttendancePage`页面组件 (`src/pages/AttendancePage/AttendancePage.tsx`)
+- [ ] 1.2 创建考勤日历组件 (`src/components/AttendanceCalendar.tsx`)
+  - 实现7列网格布局(周日到周六)
+  - 显示当前月份的完整日期
+  - 标记已打卡日期(绿色背景)
+  - 标记未打卡日期(灰色背景)
+  - 标记周末日期(不同颜色)
+- [ ] 1.3 创建前端模拟数据 (`src/utils/mockAttendanceData.ts`)
+  - 考勤统计数据
+  - 打卡明细数据(按日期倒序)
+- [ ] 1.4 实现月份选择器组件 (`src/components/MonthSelector.tsx`)
+  - 显示当前年月
+  - 左右箭头切换月份
+  - 更新日历视图和统计数据
+- [ ] 1.5 实现数据加载状态(Loading状态)
+- [ ] 1.6 实现错误处理(数据加载失败时显示错误提示)
+
+### 任务2: 实现考勤统计模块 (AC: 考勤统计展示)
+- [ ] 2.1 创建考勤统计卡片组件 (`src/components/AttendanceStats.tsx`)
+  - 显示出勤率(百分比)
+  - 显示正常出勤天数
+  - 显示异常统计(迟到、早退、缺勤次数)
+- [ ] 2.2 创建统计数据类型定义 (`src/types/attendance.ts`)
+  - AttendanceStats接口
+  - 使用RPC推断类型预留(便于后续API集成)
+- [ ] 2.3 集成前端模拟统计数据
+- [ ] 2.4 实现统计数据的响应式布局
+
+### 任务3: 实现打卡明细模块 (AC: 打卡明细列表)
+- [ ] 3.1 创建打卡明细列表组件 (`src/components/AttendanceDetails.tsx`)
+  - 显示打卡记录列表(按日期倒序)
+  - 显示日期和星期信息
+  - 显示上班/下班打卡时间
+  - 显示打卡状态标签
+- [ ] 3.2 创建打卡记录项组件 (`src/components/AttendanceRecordItem.tsx`)
+  - 日期和星期显示
+  - 打卡时间显示(上班/下班)
+  - 打卡状态标签(正常-绿色、迟到-黄色、早退-橙色、缺勤-红色)
+- [ ] 3.3 创建打卡记录类型定义 (`src/types/attendance.ts`)
+  - AttendanceRecord接口
+  - AttendanceStatus枚举类型
+- [ ] 3.4 集成前端模拟打卡明细数据
+- [ ] 3.5 实现列表滚动加载(预留分页加载接口)
+
+### 任务4: 集成Navbar导航栏组件 (AC: 页面设计与布局)
+- [ ] 4.1 导入Navbar组件: `import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'`
+- [ ] 4.2 在页面顶部添加Navbar,配置为TabBar页面(无返回按钮)
+- [ ] 4.3 Navbar配置: `leftIcon="" leftText="" onClickLeft={() => {}}`
+- [ ] 4.4 使用Navbar的`placeholder`属性占位,移除手动空白占位
+- [ ] 4.5 确保Navbar固定在顶部 (`fixed=true`)
+
+### 任务5: 更新mini-talent页面集成 (AC: 集成与兼容性)
+- [ ] 5.1 更新`mini-talent/src/pages/attendance/index.tsx`:
+  - 从`@d8d/rencai-attendance-ui/pages/AttendancePage/AttendancePage`导入AttendancePage组件
+  - 用AuthProvider包装页面
+  - 添加认证检查(未登录跳转到登录页)
+  - 导出AttendancePage组件
+- [ ] 5.2 验证页面路由配置(已在故事017.001中配置完成)
+- [ ] 5.3 验证底部TabBar导航正常工作(TabBar中"考勤"对应attendance页面)
+
+### 任务6: 实现页面样式和移动端适配 (AC: 页面设计与布局)
+- [ ] 6.1 参照原型设计实现考勤记录页样式(原型行303-481)
+  - 月份选择器样式(灰色背景、圆角、箭头图标)
+  - 考勤统计卡片样式(白色卡片、两列布局)
+  - 日历视图样式(7列网格、日期圆形、状态标记)
+  - 打卡明细列表样式(白色卡片、列表项分隔、状态标签)
+- [ ] 6.2 确保页面设计符合移动端规范:
+  - 宽度参考: 375px
+  - 圆角规范: 12px (卡片)
+  - 颜色主题: 蓝色渐变 (#3b82f6 → #1e40af)
+  - 字体规范: 标题18-24px, 正文14px, 小字12px
+  - 状态颜色: 正常-绿色、迟到-黄色、早退-橙色、缺勤-红色
+- [ ] 6.3 使用正确的组件:考勤记录页使用TabBarLayout(无返回按钮的Navbar)
+
+### 任务7: 编写测试 (AC: 集成与兼容性)
+- [ ] 7.1 为AttendancePage编写组件测试 (`tests/pages/AttendancePage/AttendancePage.test.tsx`)
+  - 测试组件渲染
+  - 测试考勤日历展示(Mock数据)
+  - 测试考勤统计展示(Mock数据)
+  - 测试打卡明细列表展示(Mock数据)
+  - 测试月份切换功能
+- [ ] 7.2 为子组件编写单元测试:
+  - AttendanceCalendar.test.tsx
+  - AttendanceStats.test.tsx
+  - AttendanceDetails.test.tsx
+  - AttendanceRecordItem.test.tsx
+  - MonthSelector.test.tsx
+- [ ] 7.3 编写集成测试验证现有功能不受影响
+- [ ] 7.4 运行`pnpm typecheck`确保类型检查通过
+
+## 开发者笔记
+
+### 前置故事见解
+
+**故事017.001完成状态:**
+- ✅ rencai系列7个UI包基础结构已创建
+- ✅ API客户端文件已创建(`src/api/index.ts`)
+- ✅ mini-talent项目路由结构已配置完成
+- ✅ 基础布局组件(TabBarLayout、Navbar等)已就绪
+- ✅ package.json的exports字段已配置
+- ✅ Jest测试框架已配置(`jest.config.cjs`)
+
+**故事017.002完成状态:**
+- ✅ 登录页面功能完整,支持人才用户身份证号/残疾证号/手机号密码登录
+- ✅ 首页/个人主页页面展示个人概览数据
+- ✅ 认证状态管理正常,token存储和验证可靠
+- ✅ AuthContext提供登录状态、用户信息和登出方法
+- ✅ 页面设计符合原型标准,移动端体验良好
+
+**故事017.003完成状态:**
+- ✅ 个人信息页面功能完整,包含个人基本信息、银行卡信息、证件照片
+- ✅ 数据脱敏工具函数实现(`maskUtils.ts`)
+- ✅ 证件照片预览功能实现
+- ✅ 测试框架选择确认:**mini项目使用Jest**(不是Vitest)
+- ✅ 使用`@d8d/mini-testing-utils`提供的Taro mock
+
+**故事017.012完成状态:**
+- ✅ 统一Navbar导航栏组件规范
+- ✅ TabBar页面使用Navbar无返回按钮(leftIcon="" leftText="")
+- ✅ 非TabBar页面使用Navbar带返回按钮
+- ✅ Navbar样式与用人方小程序保持一致
+
+**关键实现经验:**
+1. API客户端导入路径修正:从相应的后端模块包导入,而不是`@d8d/server`
+2. 页面文件简化设计:采用"薄包装层",仅导入并导出组件
+3. 复用现有共享组件:StatusBar、PageContainer、Navbar使用`@d8d/mini-shared-ui-components`中的实现
+4. 测试框架选择:**mini项目使用Jest**(不是Vitest)
+5. 前端模拟数据规范:数据结构必须符合后续API接口规范,便于后续替换为真实API
+
+### 技术栈要求
+
+**来源**: [architecture/tech-stack.md](../architecture/tech-stack.md)
+
+**运行时和框架:**
+- **Node.js**: 20.18.3
+- **Hono**: 4.8.5 (RPC客户端)
+- **React**: 19.1.0 (UI组件)
+- **Taro**: 4.1.4 (小程序框架)
+- **Tailwind CSS**: 4.1.11 (样式)
+
+**测试框架:**
+- **Jest**: 30.2.0 (mini项目使用Jest,不是Vitest!)
+- **ts-jest**: 29.4.5 (TypeScript预处理器)
+- **@testing-library/react**: 16.3.0 (React组件测试)
+- **@d8d/mini-testing-utils**: workspace包 (Taro小程序测试工具)
+
+**重要**: **mini项目使用Jest测试框架**,与web应用使用的Vitest不同。
+
+### UI包开发规范
+
+**来源**:
+- [architecture/mini-ui-package-standards.md](../architecture/mini-ui-package-standards.md) - **Mini UI包专用规范(本文档必须遵循)**
+- [architecture/ui-package-standards.md](../architecture/ui-package-standards.md) - 通用UI包规范(Web应用适用)
+
+**关键规范要求:**
+
+#### 1. UI包内部导入规范
+**重要**: UI包内部导入必须使用相对路径,不要使用别名。
+
+**正确示例**:
+```typescript
+// ✅ 正确: 使用相对路径导入同一包内的模块
+import { mockAttendanceData } from '../../utils/mockAttendanceData'
+import { AttendanceCalendar } from '../components/AttendanceCalendar'
+```
+
+**错误示例**:
+```typescript
+// ❌ 错误: 不要使用别名导入UI包内部的模块
+import { mockAttendanceData } from '@/utils/mockAttendanceData'
+import { AttendanceCalendar } from '@/components/AttendanceCalendar'
+```
+
+#### 2. package.json exports配置规范
+```json
+{
+  "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"
+    }
+  }
+}
+```
+
+#### 3. 前端模拟数据规范
+**重要**: 本故事使用前端模拟数据,数据结构必须符合后续API接口规范。
+
+**模拟数据文件位置**: `src/utils/mockAttendanceData.ts`
+
+**数据结构示例**:
+```typescript
+// 考勤统计数据(符合后续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集成预留**:
+- 类型定义使用RPC推断类型预留接口
+- 模拟数据导出函数名称与API客户端方法保持一致
+- 便于后续替换为真实API调用
+
+#### 4. Jest配置规范
+每个UI包必须创建`jest.config.cjs`配置文件,参照`rencai-personal-info-ui/jest.config.cjs`:
+
+```javascript
+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
+  - 样式和文件映射: 将样式文件和静态文件映射到mock
+- **重要**: 源代码中不使用`@/`或`~/`别名,只使用相对路径
+- `testMatch`: 支持`.spec.{ts,tsx}`和`.test.{ts,tsx}`两种测试文件格式
+
+### 原型设计参考
+
+**来源**: [docs/小程序原型/rencai.html](../小程序原型/rencai.html)
+
+**考勤记录页** (原型行303-481):
+
+#### 1. 月份选择器
+```html
+<!-- 月份选择 -->
+<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-100`
+- 圆角:12px
+- 箭头图标:Heroicons `chevron-left-20-solid`、`chevron-right-20-solid`
+- 文字大小:14px (`text-sm`)
+
+#### 2. 考勤统计卡片
+```html
+<!-- 考勤统计 -->
+<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-white`
+- 圆角:12px
+- 内边距:16px (`p-4`)
+- 卡片间距:16px (`mb-4`)
+- 统计数字字体大小:24px (`text-2xl`)
+- 异常统计字体大小:14px (`text-sm`)
+
+#### 3. 考勤日历视图
+```html
+<!-- 日历视图 -->
+<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>
+```
+
+**移动端设计规范**:
+- 7列网格布局:`grid grid-cols-7`
+- 网格间距:4px (`gap-1`)
+- 星期标题字体大小:12px (`text-xs`)
+- 日期圆形:`rounded-full`
+- 已打卡日期:绿色背景 (`bg-green-500`)
+- 未打卡日期:灰色背景 (`bg-gray-200`)
+- 周末日期:不同颜色标识
+
+#### 4. 打卡明细列表
+```html
+<!-- 打卡明细 -->
+<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-white`
+- 圆角:12px (`rounded-lg`)
+- 列表项间距:12px (`space-y-3`)
+- 日期字体大小:14px (`text-sm`)
+- 时间字体大小:12px (`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`)
+
+**整体移动端设计规范**:
+- 宽度参考: 375px
+- 状态栏高度: 44px
+- 底部导航高度: 60px
+- 圆角规范: 12px (卡片)、40px (移动框架)
+- 颜色主题: 蓝色渐变 (#3b82f6 → #1e40af)
+- 字体规范: 标题18-24px, 正文14px, 小字12px
+
+### Navbar导航栏集成规范
+
+**来源**: [docs/stories/017.012.story.md](../stories/017.012.story.md)
+
+**TabBar页面规范(考勤记录页属于此类):**
+- 使用`leftIcon=""`和`leftText=""`隐藏返回按钮
+- 参照yongren-dashboard-ui:139-148
+- Navbar组件来源: `@d8d/mini-shared-ui-components/components/navbar`
+
+**Navbar集成示例**:
+```typescript
+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](../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页面导入方式**:
+```typescript
+// 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小程序布局规范
+
+**重要**: 在Taro小程序中,`<View>` 组件内的子元素默认是**横向布局**(`flex-row`),需要显式添加 `flex flex-col` 类才能实现**垂直布局**。
+
+**正确示例**:
+```typescript
+// ✅ 正确: 使用 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>
+```
+
+**考勤统计卡片示例**:
+```typescript
+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>
+  )
+}
+```
+
+**关键点**:
+1. **统计卡片**使用水平布局(`flex justify-between`)显示两列统计
+2. **异常统计**使用水平布局(`flex justify-between`)显示三项异常
+3. **打卡明细列表**使用 `flex flex-col` 实现垂直布局
+4. **列表项**使用水平布局显示日期、时间和状态
+5. **重要**: 记住在所有需要垂直排列的 View 上添加 `flex flex-col`
+
+### 图标使用规范
+
+**来源**: [architecture/mini-ui-package-standards.md](../architecture/mini-ui-package-standards.md#图标使用规范)
+
+**重要**: **不要使用emoji**,必须使用Heroicons图标类。
+
+**图标类命名格式**: `i-heroicons-{图标名称}-{尺寸}-{样式}`
+
+**本故事需要的图标**:
+- `chevron-left-20-solid` - 左箭头(上个月)
+- `chevron-right-20-solid` - 右箭头(下个月)
+- `calendar-20-solid` - 日历图标
+
+**正确示例**:
+```typescript
+// ✅ 正确: 使用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>
+```
+
+**月份选择器图标示例**:
+```typescript
+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>
+  )
+}
+```
+
+### 日历组件实现规范
+
+**日历网格布局**:
+```typescript
+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>
+  )
+}
+```
+
+**关键点**:
+1. 使用`grid grid-cols-7`实现7列网格布局
+2. 使用`gap-1`设置网格间距
+3. 日期使用圆形背景(`rounded-full`)
+4. 已打卡日期使用绿色背景(`bg-green-500`)
+5. 未打卡日期使用灰色背景(`bg-gray-200`)
+6. 周末日期使用灰色文字(`text-gray-400`)
+
+### 打卡明细列表实现规范
+
+**列表组件结构**:
+```typescript
+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>
+  )
+}
+```
+
+**状态颜色映射**:
+```typescript
+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 '未知'
+  }
+}
+```
+
+### 前端模拟数据最佳实践
+
+**数据工厂模式**:
+```typescript
+// 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](../architecture/testing-strategy.md)
+
+**测试框架:**
+- **Jest**: 30.2.0 (mini项目使用Jest,不是Vitest!)
+- **ts-jest**: 29.4.5 (TypeScript预处理器)
+- **@testing-library/react**: 16.3.0 (React组件测试)
+- **@d8d/mini-testing-utils**: workspace包 (Taro小程序测试工具)
+
+**测试文件位置**:
+```
+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
+```
+
+**测试要求:**
+1. 为每个页面组件编写Jest测试
+2. 测试前端模拟数据的正确性
+3. 测试月份切换功能
+4. 测试日历视图的正确渲染
+5. 测试打卡明细列表的正确显示
+6. 验证mini-talent项目现有功能不受影响
+7. 运行`pnpm typecheck`确保类型检查通过
+
+**Mock响应示例**:
+```typescript
+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](../architecture/coding-standards.md)
+
+**关键编码规范:**
+
+#### 1. 必须遵循Mini UI包开发规范
+开发Mini UI包时,**必须**参考并遵循[Mini UI包开发规范](../architecture/mini-ui-package-standards.md),该规范基于史诗011和017的经验总结。
+
+#### 2. 关键检查点 (基于史诗011和017经验)
+- **Taro布局规范**: View容器默认横向布局,必须添加`flex flex-col`实现垂直布局
+- **图标使用规范**: 必须使用Heroicons图标类,不要使用emoji
+- **测试框架选择**: **mini项目使用Jest**(不是Vitest)
+- **Navbar集成**: TabBar页面使用Navbar无返回按钮
+
+#### 3. 常见错误避免
+- ❌ 不要忘记添加 `flex flex-col` 实现垂直布局
+- ❌ 不要使用emoji代替Heroicons图标
+- ❌ 不要忘记为图标添加尺寸类(`w-5 h-5`、`text-lg`等)
+- ❌ 不要在Mini UI包内部导入中使用别名 (`@/`、`~/`等),必须使用相对路径
+- ❌ 不要在TabBar页面添加返回按钮
+- ❌ 不要使用Vitest作为Mini项目的测试框架(应使用Jest)
+
+**路径使用示例**:
+```typescript
+// ✅ 正确: 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'
+```
+
+#### 4. 参考实现
+- **用人方考勤UI包**: `mini-ui-packages/yongren-attendance-ui` (如果存在)
+- **用人方仪表板UI包**: `mini-ui-packages/yongren-dashboard-ui`
+  - 组件: `src/pages/Dashboard/Dashboard.tsx`
+  - package.json exports配置
+  - 目录结构参考
+  - Jest配置: `jest.config.cjs`
+- **人才个人信息UI包**: `mini-ui-packages/rencai-personal-info-ui`
+  - 组件结构和测试参考
+  - 前端模拟数据实现参考
+
+### 技术约束
+
+1. **向后兼容**: 不影响现有mini-talent项目功能
+2. **类型安全**: 使用TypeScript严格模式,所有数据结构必须有类型定义
+3. **模块独立性**: 每个UI包独立管理自己的类型定义和模拟数据
+4. **测试覆盖**: 所有新增代码必须有测试覆盖
+5. **代码规范**: 遵循项目编码标准和Mini UI包开发规范
+6. **数据规范**: 前端模拟数据结构符合后续API接口规范,便于后续替换为真实API
+
+### 风险和缓解措施
+
+**主要风险:**
+1. **API延期风险**: 史诗015的考勤记录API为P2延期功能,使用前端模拟数据
+2. **日历组件复杂度**: 日历视图实现可能较为复杂,需要处理月份切换和日期计算
+3. **数据结构变更**: 前端模拟数据结构与后续API接口可能存在差异
+4. **UI组件复用风险**: rencai系列UI包可能与现有yongren系列UI包存在差异
+
+**缓解措施:**
+1. **前端模拟数据规范**: 严格按照后续API接口规范设计数据结构,便于后续替换
+2. **分阶段实现**: 先实现考勤统计和打卡明细,再实现日历视图
+3. **参考现有模式**: 参照yongren系列UI包的实现模式和架构
+4. **类型安全**: 使用TypeScript接口定义数据结构,确保类型一致性
+5. **测试驱动**: 编写完整的测试,确保功能正确性
+6. **数据工厂模式**: 使用数据工厂模式生成模拟数据,便于后续替换为真实API
+
+## 测试
+
+### 测试框架和模式
+
+**来源**: [architecture/testing-strategy.md](../architecture/testing-strategy.md)
+
+**测试框架:**
+- **Jest**: 30.2.0 (mini项目使用Jest,不是Vitest!)
+- **ts-jest**: 29.4.5 (TypeScript预处理器)
+- **@testing-library/react**: 16.3.0 (React组件测试)
+- **@d8d/mini-testing-utils**: workspace包 (Taro小程序测试工具)
+
+**测试文件位置:**
+```
+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
+```
+
+### 测试要求
+
+1. **组件测试**:
+   - 测试组件渲染正确
+   - 测试用户交互(月份切换、点击日期等)
+   - 测试前端模拟数据集成
+   - 测试错误处理
+
+2. **日历组件测试**:
+   - 测试日历网格的正确渲染
+   - 测试日期标记的正确显示
+   - 测试周末日期的正确标识
+   - 测试月份切换功能
+
+3. **统计组件测试**:
+   - 测试统计数据的正确显示
+   - 测试数据格式化(百分比、天数、次数)
+
+4. **打卡明细测试**:
+   - 测试打卡记录列表的正确渲染
+   - 测试打卡状态标签的颜色
+   - 测试日期倒序排列
+
+5. **集成测试**:
+   - 测试考勤记录页面的三个模块数据加载
+   - 测试Navbar导航栏的正确显示
+   - 测试月份切换后数据和视图的更新
+
+6. **回归测试**:
+   - 验证mini-talent项目现有功能不受影响
+   - 运行`pnpm typecheck`确保类型检查通过
+
+### 测试执行
+
+```bash
+# 运行所有测试
+cd mini-ui-packages/rencai-attendance-ui && pnpm test
+
+# 运行特定测试
+pnpm test --testNamePattern="AttendancePage"
+
+# 生成覆盖率报告
+pnpm test:coverage
+```
+
+## 变更日志
+
+| 日期 | 版本 | 描述 | 作者 |
+|------|------|------|------|
+| 2025-12-26 | 1.0 | 创建故事文档 | Bob (Scrum Master) |
+
+## 开发者记录
+
+*此部分由开发代理在实施过程中填写*
+
+### 使用的代理模型
+
+待填写
+
+### 调试日志引用
+
+待填写
+
+### 完成说明列表
+
+待填写
+
+### 文件列表
+
+待填写
+
+## QA结果
+
+*此部分由QA代理在审查完成后填写*