Przeglądaj źródła

docs: 添加Mini UI包开发规范和故事017.003

- 新增 Mini UI包开发规范文档 (mini-ui-package-standards.md)
  - 基于史诗011和017的实施经验总结
  - Taro小程序核心布局规范 (flex flex-col)
  - 图标使用规范 (Heroicons, 不使用emoji)
  - Navbar导航栏集成规范
  - 照片预览、数据脱敏等实现规范

- 新增故事017.003 (个人信息功能实现)
  - 个人基本信息、银行卡信息、证件照片展示
  - 证件照片预览功能
  - 完整的开发者笔记和技术上下文

- 更新编码标准文档 (coding-standards.md)
  - 添加Mini UI包开发提示章节
  - 5个关键检查点和常见错误避免

- 更新架构文档索引 (index.md)
  - 添加Mini UI包开发规范的目录链接

🤖 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 3 tygodni temu
rodzic
commit
540e0dd21d

+ 87 - 0
docs/architecture/coding-standards.md

@@ -3,6 +3,7 @@
 ## 版本信息
 | 版本 | 日期 | 描述 | 作者 |
 |------|------|------|------|
+| 2.5 | 2025-12-26 | 添加Mini UI包开发规范章节 | Bob (Scrum Master) |
 | 2.4 | 2025-09-20 | 与主架构文档版本一致 | Winston |
 
 ## 现有标准合规性
@@ -11,6 +12,7 @@
 - **测试模式**: 完整的测试框架已配置(Vitest + Testing Library + Playwright)
 - **文档风格**: 代码注释良好,测试策略文档完整
 - **UI包开发**: 必须遵循[UI包开发规范](./ui-package-standards.md)(基于史诗008经验总结)
+- **Mini UI包开发**: 必须遵循[Mini UI包开发规范](./mini-ui-package-standards.md)(基于史诗011和017经验总结)
 
 ## 增强特定标准
 - **测试框架**: 使用Vitest + Testing Library + hono/testing + Playwright
@@ -46,3 +48,88 @@
 - 广告管理UI包:`packages/advertisement-management-ui`
 - 平台管理UI包:`allin-packages/platform-management-ui`
 - 渠道管理UI包:`allin-packages/channel-management-ui`(史诗008.002)
+
+## Mini UI包开发提示
+
+### 必须遵循的规范
+开发Mini UI包(Taro小程序UI包)时,**必须**参考并遵循[Mini UI包开发规范](./mini-ui-package-standards.md),该规范基于史诗011(用人方小程序)和史诗017(人才小程序)的实施经验总结。
+
+### 关键检查点(基于史诗011和017经验)
+
+#### 1. Taro小程序布局规范
+- **View组件默认横向布局**:View容器内的子元素默认是横向布局(`flex-row`),必须显式添加 `flex flex-col` 类才能实现垂直布局
+- **Text组件默认内联显示**:Text组件默认是内联显示(类似span),需要使用`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>
+```
+
+#### 2. 图标使用规范
+- **必须使用Heroicons图标类**:不要使用emoji或文本符号
+- **图标类命名格式**:`i-heroicons-{icon-name}-{size}-{style}`
+- **必须添加尺寸类**:如 `w-5 h-5`、`text-lg` 等
+
+**正确示例**:
+```typescript
+// ✅ 正确: 使用Heroicons图标类
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-600" />
+<View className="i-heroicons-user-20-solid w-6 h-6 text-blue-500" />
+<View className="i-heroicons-bell-20-solid w-4 h-4 text-gray-400" />
+
+// ❌ 错误: 使用emoji
+<Text>🔔</Text>
+<Text>👤</Text>
+<View>←</View>
+```
+
+**常用图标**:
+- `chevron-left-20-solid` - 左箭头(返回按钮)
+- `user-20-solid` - 用户
+- `bell-20-solid` - 通知铃
+- `document-text-20-solid` - 文档
+- `chart-bar-20-solid` - 图表
+- `calendar-20-solid` - 日历
+- `phone-20-solid` - 电话
+- `lock-closed-20-solid` - 锁
+- `qr-code-20-solid` - 二维码
+
+#### 3. Navbar导航栏集成规范
+- **TabBar页面(一级)**:使用Navbar无返回按钮(`leftIcon=""`、`leftText=""`)
+- **非TabBar页面(二级)**:使用Navbar带返回按钮(`leftIcon="i-heroicons-chevron-left-20-solid"`)
+- **Navbar组件来源**:`@d8d/mini-shared-ui-components/components/navbar`
+
+#### 4. 测试框架选择
+- **Mini项目使用Jest**:不是Vitest(与Web应用不同)
+- **测试配置文件**:`jest.config.cjs`
+- **测试工具包**:`@d8d/mini-testing-utils`
+
+#### 5. API客户端模式
+- **每个UI包独立管理**:每个Mini UI包包含自己的API客户端和RPC类型
+- **使用相对路径导入**:UI包内部必须使用相对路径,不要使用别名
+- **RPC推断类型**:必须使用RPC推断类型,而不是直接导入schema类型
+
+### 常见错误避免
+- ❌ 不要忘记添加 `flex flex-col` 实现垂直布局
+- ❌ 不要使用emoji代替Heroicons图标
+- ❌ 不要忘记为图标添加尺寸类(`w-5 h-5`、`text-lg`等)
+- ❌ 不要在Mini UI包内部导入中使用别名(`@/`、`~/`等)
+- ❌ 不要使用Vitest作为Mini项目的测试框架(应使用Jest)
+
+### 参考实现
+- 用人方小程序UI包:`mini-ui-packages/yongren-dashboard-ui`
+- 人才小程序UI包:`mini-ui-packages/rencai-dashboard-ui`
+- 共享组件包:`mini-ui-packages/mini-shared-ui-components`
+

+ 15 - 0
docs/architecture/index.md

@@ -43,6 +43,8 @@
     - [现有标准合规性](./coding-standards.md#现有标准合规性)
     - [增强特定标准](./coding-standards.md#增强特定标准)
     - [关键集成规则](./coding-standards.md#关键集成规则)
+    - [UI包开发提示](./coding-standards.md#ui包开发提示)
+    - [Mini UI包开发提示](./coding-standards.md#mini-ui包开发提示)
   - [后端模块包开发规范](./backend-module-package-standards.md)
     - [包结构规范](./backend-module-package-standards.md#包结构规范)
     - [实体设计规范](./backend-module-package-standards.md#实体设计规范)
@@ -67,6 +69,19 @@
     - [错误处理规范](./ui-package-standards.md#错误处理规范)
     - [开发流程规范](./ui-package-standards.md#开发流程规范)
     - [参考实现](./ui-package-standards.md#参考实现)
+  - [Mini UI包开发规范](./mini-ui-package-standards.md)
+    - [概述](./mini-ui-package-standards.md#概述)
+    - [Taro小程序核心布局规范](./mini-ui-package-standards.md#taro小程序核心布局规范)
+    - [图标使用规范](./mini-ui-package-standards.md#图标使用规范)
+    - [Taro组件使用规范](./mini-ui-package-standards.md#taro组件使用规范)
+    - [Navbar导航栏集成规范](./mini-ui-package-standards.md#navbar导航栏集成规范)
+    - [照片预览功能实现](./mini-ui-package-standards.md#照片预览功能实现)
+    - [数据脱敏规范](./mini-ui-package-standards.md#数据脱敏规范)
+    - [Mini UI包结构规范](./mini-ui-package-standards.md#mini-ui包结构规范)
+    - [测试规范](./mini-ui-package-standards.md#测试规范)
+    - [常见问题和解决方案](./mini-ui-package-standards.md#常见问题和解决方案)
+    - [最佳实践](./mini-ui-package-standards.md#最佳实践)
+    - [参考实现](./mini-ui-package-standards.md#参考实现)
   - [安全集成](./security-integration.md)
     - [现有安全措施](./security-integration.md#现有安全措施)
     - [增强安全要求](./security-integration.md#增强安全要求)

+ 866 - 0
docs/architecture/mini-ui-package-standards.md

@@ -0,0 +1,866 @@
+# Mini UI包开发规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.1 | 2025-12-26 | 添加图标使用规范(Heroicons) | Bob (Scrum Master) |
+| 1.0 | 2025-12-26 | 基于史诗011和017经验创建Mini UI包开发规范 | Bob (Scrum Master) |
+
+## 概述
+
+本文档专门针对Taro小程序UI包(mini-ui-packages)的开发规范,基于史诗011(用人方小程序)和史诗017(人才小程序)的实施经验总结。Mini UI包与Web UI包有显著的差异,特别是在布局、组件行为和平台特性方面。
+
+**适用范围:**
+- `mini-ui-packages/` 目录下的所有UI包
+- 使用Taro框架的小程序项目
+- 所有基于`@tarojs/components`的组件开发
+
+## Taro小程序核心布局规范
+
+### 1. View组件的默认布局行为
+
+**重要**: 在Taro小程序中,`<View>` 组件内的子元素默认是**横向布局**(`flex-row`),这与Web开发的div默认垂直布局行为完全不同。
+
+#### 1.1 垂直布局规范
+
+**问题**: View容器默认横向布局,导致子元素横向排列
+
+**解决方案**: 必须显式添加 `flex flex-col` 类才能实现垂直布局
+
+**正确示例**:
+```typescript
+import { View, Text } from '@tarojs/components'
+
+// ✅ 正确: 使用 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>
+```
+
+#### 1.2 信息卡片布局模式
+
+**来源**: 史诗011.003经验总结
+
+**标准模式**:
+```typescript
+import { View, Text } from '@tarojs/components'
+
+export function PersonalBasicInfo({ personalInfo }: { personalInfo: PersonalInfoResponse }) {
+  return (
+    <View className="bg-white rounded-lg p-4">
+      <Text className="text-lg font-semibold mb-4">个人基本信息</Text>
+
+      {/* 垂直布局的信息列表 - 必须使用 flex flex-col */}
+      <View className="flex flex-col space-y-2">
+        <View className="flex justify-between">
+          <Text className="text-gray-600">姓名</Text>
+          <Text>{personalInfo.name}</Text>
+        </View>
+
+        <View className="flex justify-between">
+          <Text className="text-gray-600">性别</Text>
+          <Text>{personalInfo.gender}</Text>
+        </View>
+
+        <View className="flex justify-between">
+          <Text className="text-gray-600">年龄</Text>
+          <Text>{personalInfo.age}</Text>
+        </View>
+
+        {/* 更多字段... */}
+      </View>
+    </View>
+  )
+}
+```
+
+**关键点**:
+1. **信息列表容器**必须使用 `flex flex-col` 实现垂直布局
+2. **每个信息项**使用 `flex justify-between` 实现标签和值的左右分布
+3. 使用 `space-y-2` 或 `space-y-3` 添加垂直间距
+4. **重要**: 记住在所有需要垂直排列的 View 上添加 `flex flex-col`
+
+### 2. Text组件的默认内联行为
+
+**问题**: Text组件默认是内联显示(类似Web中的`<span>`),不会自动换行
+
+**影响**: 导致多个Text组件在同一行显示,即使它们在不同的代码行上
+
+**解决方案**: 使用`flex flex-col`强制Text组件垂直排列
+
+**实际案例** (来源: 史诗011.003):
+```typescript
+// ❌ 错误: Text组件会内联显示在同一行
+<View>
+  <Text>统计1</Text>
+  <Text>统计2</Text>
+  <Text>统计3</Text>
+</View>
+
+// ✅ 正确: 使用 flex flex-col 强制垂直排列
+<View className="flex flex-col">
+  <Text>统计1</Text>
+  <Text>统计2</Text>
+  <Text>统计3</Text>
+</View>
+```
+
+### 3. 常见布局模式
+
+#### 3.1 卡片容器布局
+
+```typescript
+<View className="bg-white rounded-lg p-4 shadow-sm">
+  <Text className="text-lg font-semibold mb-4">卡片标题</Text>
+
+  {/* 内容区域 - 垂直布局 */}
+  <View className="flex flex-col space-y-3">
+    {items.map(item => (
+      <View key={item.id} className="flex justify-between border-b pb-2">
+        <Text className="text-gray-600">{item.label}</Text>
+        <Text>{item.value}</Text>
+      </View>
+    ))}
+  </View>
+</View>
+```
+
+#### 3.2 列表项布局
+
+```typescript
+<View className="flex flex-col space-y-2">
+  {list.map(item => (
+    <View key={item.id} className="bg-white rounded-lg p-3">
+      {/* 列表项标题 */}
+      <Text className="font-semibold mb-2">{item.title}</Text>
+
+      {/* 列表项内容 - 垂直布局 */}
+      <View className="flex flex-col space-y-1">
+        <Text className="text-sm text-gray-600">{item.description}</Text>
+        <Text className="text-xs text-gray-400">{item.date}</Text>
+      </View>
+    </View>
+  ))}
+</View>
+```
+
+#### 3.3 网格布局
+
+```typescript
+{/* 2列网格 */}
+<View className="grid grid-cols-2 gap-3">
+  {items.map(item => (
+    <View key={item.id} className="bg-white rounded-lg p-3">
+      <View className="flex flex-col">
+        <Text className="font-semibold">{item.title}</Text>
+        <Text className="text-sm text-gray-600">{item.value}</Text>
+      </View>
+    </View>
+  ))}
+</View>
+```
+
+## 图标使用规范
+
+### 3.1 图标系统概述
+
+**项目使用的图标库**: Heroicons (UnoCSS图标集)
+
+**图标类命名规范**: `i-heroicons-{icon-name}-{size}-{style}`
+
+**重要**: **不要使用emoji**,必须使用Heroicons图标类。
+
+### 3.2 图标类使用规范
+
+#### 3.2.1 基础图标使用
+
+**正确示例**:
+```typescript
+import { View } from '@tarojs/components'
+
+// ✅ 正确: 使用Heroicons图标类
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-600" />
+<View className="i-heroicons-user-20-solid w-6 h-6 text-blue-500" />
+<View className="i-heroicons-bell-20-solid w-4 h-4 text-gray-400" />
+
+// ❌ 错误: 使用emoji
+<Text>🔔</Text>
+<Text>👤</Text>
+<View>←</View>
+```
+
+#### 3.2.2 图标类命名格式
+
+**格式**: `i-heroicons-{图标名称}-{尺寸}-{样式}`
+
+**常用图标名称**:
+- `chevron-left` - 左箭头
+- `chevron-right` - 右箭头
+- `user` - 用户
+- `bell` - 通知铃
+- `document-text` - 文档
+- `chart-bar` - 图表
+- `building-office` - 建筑/企业
+- `calendar` - 日历
+- `phone` - 电话
+- `lock-closed` - 锁
+- `camera` - 相机
+- `qr-code` - 二维码
+- `device-phone-mobile` - 手机
+- `arrow-right-on-rectangle` - 登出/外跳
+- `arrow-left-on-rectangle` - 登入/内跳
+- `exclamation-triangle` - 警告
+- `exclamation-circle` - 提示
+- `photo` - 图片
+- `arrow-path` - 加载中
+
+**尺寸选项**:
+- `20` - 20x20 (推荐用于小程序)
+- `24` - 24x24
+
+**样式选项**:
+- `solid` - 实心图标(推荐)
+- `outline` - 轮廓图标
+
+#### 3.2.3 图标尺寸和颜色
+
+**尺寸类**:
+```typescript
+<View className="i-heroicons-user-20-solid w-4 h-4" />   // 16px
+<View className="i-heroicons-user-20-solid w-5 h-5" />   // 20px
+<View className="i-heroicons-user-20-solid w-6 h-6" />   // 24px
+<View className="i-heroicons-user-20-solid w-8 h-8" />   // 32px
+<View className="i-heroicons-user-20-solid text-xl" />  // 使用Tailwind文本尺寸
+<View className="i-heroicons-user-20-solid text-2xl" /> // 使用Tailwind文本尺寸
+```
+
+**颜色类**:
+```typescript
+<View className="i-heroicons-user-20-solid text-gray-400" />  // 灰色
+<View className="i-heroicons-user-20-solid text-blue-500" />  // 蓝色
+<View className="i-heroicons-user-20-solid text-green-500" /> // 绿色
+<View className="i-heroicons-user-20-solid text-red-500" />   // 红色
+<View className="i-heroicons-user-20-solid text-white" />    // 白色
+<View className="i-heroicons-user-20-solid text-yellow-600" /> // 黄色
+<View className="i-heroicons-user-20-solid text-purple-500" /> // 紫色
+```
+
+#### 3.2.4 常见使用场景
+
+**导航栏返回按钮**:
+```typescript
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5" />
+```
+
+**功能入口图标**:
+```typescript
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-user-20-solid text-blue-500 text-lg mb-1" />
+  <Text className="text-sm">个人信息</Text>
+</View>
+
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-document-text-20-solid text-green-500 text-lg mb-1" />
+  <Text className="text-sm">考勤记录</Text>
+</View>
+
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-chart-bar-20-solid text-purple-500 text-lg mb-1" />
+  <Text className="text-sm">薪资查询</Text>
+</View>
+
+<View className="flex flex-col items-center">
+  <View className="i-heroicons-building-office-2-20-solid text-yellow-600 text-lg mb-1" />
+  <Text className="text-sm">企业信息</Text>
+</View>
+```
+
+**状态指示器**:
+```typescript
+// 加载中
+<View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5" />
+
+// 成功/提示
+<View className="i-heroicons-check-circle-20-solid text-green-500 w-6 h-6" />
+
+// 警告
+<View className="i-heroicons-exclamation-triangle-20-solid text-orange-500 w-6 h-6" />
+
+// 错误
+<View className="i-heroicons-x-circle-20-solid text-red-500 w-6 h-6" />
+
+// 信息
+<View className="i-heroicons-information-circle-20-solid text-blue-500 w-6 h-6" />
+```
+
+**输入框图标**:
+```typescript
+<View className="flex items-center">
+  <View className="i-heroicons-phone-20-solid text-gray-400 mr-3 w-5 h-5" />
+  <Input placeholder="请输入手机号" />
+</View>
+
+<View className="flex items-center">
+  <View className="i-heroicons-lock-closed-20-solid text-gray-400 mr-3 w-5 h-5" />
+  <Input placeholder="请输入密码" type="password" />
+</View>
+```
+
+**二维码按钮**:
+```typescript
+<View className="flex items-center">
+  <Text>张三</Text>
+  <View className="i-heroicons-qr-code-20-solid text-white text-lg ml-2" />
+</View>
+```
+
+#### 3.2.5 动画效果
+
+**旋转动画**:
+```typescript
+<View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5" />
+```
+
+**脉冲动画**:
+```typescript
+<View className="i-heroicons-bell-20-solid animate-pulse w-6 h-6" />
+```
+
+### 3.3 Navbar图标规范
+
+**返回按钮图标**:
+```typescript
+leftIcon = 'i-heroicons-chevron-left-20-solid'
+```
+
+**示例**:
+```typescript
+<Navbar
+  title="页面标题"
+  leftIcon="i-heroicons-chevron-left-20-solid"
+  leftText="返回"
+  onClickLeft={() => Taro.navigateBack()}
+/>
+```
+
+### 3.4 TabBar图标规范
+
+**使用iconClass属性**(推荐):
+```typescript
+const tabList = [
+  {
+    title: '首页',
+    iconClass: 'i-heroicons-home-20-solid',
+    selectedIconClass: 'i-heroicons-home-20-solid',
+    pagePath: '/pages/index/index'
+  },
+  {
+    title: '考勤',
+    iconClass: 'i-heroicons-calendar-20-solid',
+    selectedIconClass: 'i-heroicons-calendar-20-solid',
+    pagePath: '/pages/attendance/index'
+  }
+]
+```
+
+### 3.5 图标查找参考
+
+**Heroicons官方图标库**: https://heroicons.com/
+
+**使用UnoCSS图标集**: 项目使用UnoCSS的Heroicons图标集,所有图标名称遵循Heroicons命名规范。
+
+**查找图标的方法**:
+1. 访问 Heroicons 官网查找所需图标
+2. 记录图标名称(如 `user`, `chevron-left`)
+3. 使用格式: `i-heroicons-{图标名称}-20-solid`
+4. 添加尺寸和颜色类
+
+### 3.6 常见错误避免
+
+**错误示例**:
+```typescript
+// ❌ 错误1: 使用emoji
+<Text>🔔 通知</Text>
+<View>👤</View>
+
+// ❌ 错误2: 使用文本符号
+<Text>← 返回</Text>
+<View>→</View>
+
+// ❌ 错误3: 使用其他图标库
+<View className="fa fa-user" />
+<View className="material-icons">person</View>
+
+// ❌ 错误4: 忘记添加尺寸类
+<View className="i-heroicons-user-20-solid" />  {/* 没有尺寸,可能不显示 */}
+
+// ❌ 错误5: 图标类名拼写错误
+<View className="i-heroicon-user-20-solid" />   {/* 缺少s */}
+<View className="i-heroicons-user-20-solid" />   {/* ✅ 正确 */}
+```
+
+**正确示例**:
+```typescript
+// ✅ 正确1: 使用Heroicons图标类
+<View className="i-heroicons-bell-20-solid w-5 h-5" />
+<View className="i-heroicons-user-20-solid w-6 h-6" />
+
+// ✅ 正确2: 添加尺寸和颜色
+<View className="i-heroicons-chevron-left-20-solid w-5 h-5 text-gray-600" />
+
+// ✅ 正确3: 图标+文本组合
+<View className="flex items-center">
+  <View className="i-heroicons-phone-20-solid text-blue-500 w-5 h-5 mr-2" />
+  <Text>联系电话</Text>
+</View>
+```
+
+## Taro组件使用规范
+
+### 4.1 基础组件导入
+
+```typescript
+import { View, Text, Image, Button, ScrollView } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+```
+
+### 4.2 Image组件规范
+
+```typescript
+<Image
+  src={imageUrl}
+  mode="aspectFill"  // 或 aspectFit, widthFix
+  className="w-full h-32 rounded-lg"
+  lazyLoad          // 懒加载
+  onClick={handleClick}
+/>
+```
+
+**mode模式说明**:
+- `aspectFill`: 保持纵横比缩放图片,确保图片填充整个容器(可能裁剪)
+- `aspectFit`: 保持纵横比缩放图片,确保图片完全显示(可能有空白)
+- `widthFix`: 宽度不变,高度自动变化,保持原图宽高比
+
+### 4.3 ScrollView组件规范
+
+```typescript
+<ScrollView
+  scrollY           // 垂直滚动
+  className="h-full"
+  onScrollToLower={handleLoadMore}
+  lowerThreshold={100}
+>
+  <View className="flex flex-col">
+    {items.map(item => (
+      <View key={item.id}>{item.content}</View>
+    ))}
+  </View>
+</ScrollView>
+```
+
+### 4.4 Button组件规范
+
+**注意**: Taro的Button组件有默认样式,如需自定义样式建议使用View
+
+```typescript
+// ✅ 推荐: 使用View实现自定义按钮
+<View
+  className="bg-blue-500 text-white py-2 px-4 rounded text-center"
+  onClick={handleClick}
+>
+  <Text>确定</Text>
+</View>
+
+// ⚠️ 谨慎使用: Taro Button组件有平台默认样式
+<Button onClick={handleClick}>确定</Button>
+```
+
+## Navbar导航栏集成规范
+
+### 5.1 Navbar组件来源
+
+```typescript
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+```
+
+### 5.2 页面层级划分
+
+**TabBar页面(一级,无返回按钮)**:
+- 首页/个人主页
+- 列表页
+- 个人信息页
+- 设置页
+
+**非TabBar页面(二级,带返回按钮)**:
+- 详情页
+- 编辑页
+- 从其他页面跳转来的页面
+
+### 5.3 Navbar配置规范
+
+**TabBar页面(无返回按钮)**:
+```typescript
+<Navbar
+  title="页面标题"
+  leftIcon=""
+  leftText=""
+  onClickLeft={() => {}}
+  placeholder
+  fixed
+/>
+```
+
+**非TabBar页面(带返回按钮)**:
+```typescript
+import Taro from '@tarojs/taro'
+
+<Navbar
+  title="页面标题"
+  leftIcon="i-heroicons-chevron-left-20-solid"
+  leftText="返回"
+  onClickLeft={() => Taro.navigateBack()}
+  placeholder
+  fixed
+/>
+```
+
+### 5.4 完整页面结构示例
+
+```typescript
+import { View, ScrollView } from '@tarojs/components'
+import { Navbar } from '@d8d/mini-shared-ui-components/components/navbar'
+
+export function MyPage() {
+  return (
+    <View className="h-screen bg-gray-100">
+      {/* Navbar导航栏 */}
+      <Navbar
+        title="页面标题"
+        leftIcon=""
+        leftText=""
+        onClickLeft={() => {}}
+        placeholder
+        fixed
+      />
+
+      {/* 页面内容 */}
+      <ScrollView scrollY className="h-full">
+        <View className="flex flex-col space-y-3 p-4">
+          {/* 页面内容 */}
+        </View>
+      </ScrollView>
+    </View>
+  )
+}
+```
+
+## 照片预览功能实现
+
+### 6.1 使用Taro.previewImage
+
+```typescript
+import Taro from '@tarojs/taro'
+import { View, Image, Text } from '@tarojs/components'
+
+interface DocumentPhotoItemProps {
+  type: string
+  url: string
+}
+
+export function DocumentPhotoItem({ type, url }: DocumentPhotoItemProps) {
+  const handlePreview = () => {
+    Taro.previewImage({
+      current: url,    // 当前显示图片的http链接
+      urls: [url]      // 需要预览的图片http链接列表
+    })
+  }
+
+  return (
+    <View onClick={handlePreview} className="flex flex-col">
+      <Image
+        src={url}
+        mode="aspectFill"
+        className="w-full h-32 rounded-lg"
+      />
+      <Text>{type}</Text>
+    </View>
+  )
+}
+```
+
+**多图片预览**:
+```typescript
+const handlePreview = (currentIndex: number) => {
+  Taro.previewImage({
+    current: images[currentIndex].url,
+    urls: images.map(img => img.url)
+  })
+}
+```
+
+## 数据脱敏规范
+
+### 7.1 银行卡号脱敏
+
+```typescript
+/**
+ * 脱敏银行卡号
+ * @param cardNumber 完整银行卡号
+ * @returns 脱敏后的银行卡号(如:**** **** **** 1234)
+ */
+export function maskCardNumber(cardNumber: string): string {
+  if (!cardNumber || cardNumber.length < 4) {
+    return '****'
+  }
+  const last4 = cardNumber.slice(-4)
+  return `**** **** **** ${last4}`
+}
+
+// 使用示例
+<View className="flex justify-between">
+  <Text className="text-gray-600">银行卡号</Text>
+  <Text>{maskCardNumber(bankCard.cardNumber)}</Text>
+</View>
+```
+
+### 7.2 身份证号脱敏
+
+```typescript
+/**
+ * 脱敏身份证号
+ * @param idCard 完整身份证号
+ * @returns 脱敏后的身份证号(如:3301**********1234)
+ */
+export function maskIdCard(idCard: string): string {
+  if (!idCard || idCard.length < 8) {
+    return '********'
+  }
+  const prefix = idCard.slice(0, 4)
+  const suffix = idCard.slice(-4)
+  return `${prefix}**********${suffix}`
+}
+
+// 使用示例
+<View className="flex justify-between">
+  <Text className="text-gray-600">身份证号</Text>
+  <Text>{maskIdCard(personalInfo.idCard)}</Text>
+</View>
+```
+
+### 7.3 手机号脱敏
+
+```typescript
+/**
+ * 脱敏手机号
+ * @param phone 完整手机号
+ * @returns 脱敏后的手机号(如:138****5678)
+ */
+export function maskPhone(phone: string): string {
+  if (!phone || phone.length < 7) {
+    return '****'
+  }
+  return `${phone.slice(0, 3)}****${phone.slice(-4)}`
+}
+```
+
+## Mini UI包结构规范
+
+### 8.1 标准目录结构
+
+```text
+mini-ui-packages/<package-name>/
+├── src/
+│   ├── pages/                    # 页面组件
+│   │   └── PageName/
+│   │       ├── PageName.tsx
+│   │       └── index.ts
+│   ├── components/               # UI组件
+│   │   ├── ComponentName.tsx
+│   │   └── index.ts
+│   ├── api/                      # API客户端
+│   │   ├── client.ts
+│   │   └── index.ts
+│   ├── utils/                    # 工具函数
+│   │   ├── helpers.ts
+│   │   └── index.ts
+│   └── index.ts                  # 主入口
+├── tests/                        # 测试文件
+│   ├── pages/
+│   │   └── PageName/
+│   │       └── PageName.test.tsx
+│   └── components/
+│       └── ComponentName.test.tsx
+├── package.json
+├── jest.config.cjs               # Jest配置
+└── tsconfig.json
+```
+
+### 8.2 package.json配置
+
+```json
+{
+  "name": "@d8d/<package-name>",
+  "version": "1.0.0",
+  "type": "module",
+  "main": "src/index.ts",
+  "types": "src/index.ts",
+  "exports": {
+    ".": {
+      "types": "./src/index.ts",
+      "import": "./src/index.ts"
+    },
+    "./api": {
+      "types": "./src/api/index.ts",
+      "import": "./src/api/index.ts"
+    },
+    "./pages/<PageName>/<PageName>": {
+      "types": "./src/pages/<PageName>/<PageName>.tsx",
+      "import": "./src/pages/<PageName>/<PageName>.tsx"
+    }
+  },
+  "dependencies": {
+    "@d8d/mini-shared-ui-components": "workspace:*",
+    "@tarojs/components": "^4.1.4",
+    "@tarojs/taro": "^4.1.4",
+    "react": "^19.1.0"
+  },
+  "devDependencies": {
+    "@testing-library/react": "^16.3.0",
+    "jest": "^30.2.0",
+    "ts-jest": "^29.4.5"
+  }
+}
+```
+
+## 测试规范
+
+### 9.1 Jest配置
+
+```javascript
+module.exports = {
+  preset: 'ts-jest',
+  testEnvironment: 'jsdom',
+  setupFilesAfterEnv: ['@d8d/mini-testing-utils/setup'],
+  moduleNameMapper: {
+    '^@tarojs/taro$': '@d8d/mini-testing-utils/testing/taro-api-mock.ts',
+    '\\.(css|less|scss)$': '@d8d/mini-testing-utils/testing/style-mock.js'
+  },
+  testMatch: [
+    '<rootDir>/tests/**/*.test.{ts,tsx}'
+  ],
+  transform: {
+    '^.+\\.(ts|tsx)$': 'ts-jest'
+  }
+}
+```
+
+### 9.2 组件测试示例
+
+```typescript
+import { render, screen } from '@testing-library/react'
+import { View, Text } from '@tarojs/components'
+import { MyComponent } from '../MyComponent'
+
+describe('MyComponent', () => {
+  it('渲染组件并验证垂直布局', () => {
+    render(<MyComponent />)
+
+    // 验证组件包含 flex flex-col 类
+    const container = screen.getByTestId('my-container')
+    expect(container.className).toContain('flex flex-col')
+  })
+})
+```
+
+## 常见问题和解决方案
+
+### 10.1 布局问题
+
+**问题**: 元素横向排列而不是垂直排列
+- **原因**: View容器默认是flex-row
+- **解决**: 添加`flex flex-col`类
+
+**问题**: Text组件在同一行显示
+- **原因**: Text组件默认是内联显示
+- **解决**: 父容器添加`flex flex-col`类
+
+### 10.2 样式问题
+
+**问题**: Tailwind样式不生效
+- **原因**: 类名冲突或拼写错误
+- **解决**: 检查类名拼写,确保使用正确的Tailwind类名
+
+**问题**: 样式在不同平台表现不一致
+- **原因**: 不同小程序平台的样式引擎差异
+- **解决**: 使用Taro提供的跨平台样式方案,避免使用平台特有样式
+
+### 10.3 API问题
+
+**问题**: RPC客户端类型错误
+- **原因**: API路径映射错误或类型推断不正确
+- **解决**: 验证后端路由定义,使用RPC推断类型
+
+## 最佳实践
+
+### 11.1 组件开发
+
+1. **始终使用flex flex-col实现垂直布局**
+2. **为每个View添加语义化的className**
+3. **使用data-testid属性便于测试**
+4. **组件props使用TypeScript接口定义**
+5. **使用相对路径导入包内模块**
+
+### 11.2 性能优化
+
+1. **使用Image组件的lazyLoad属性**
+2. **列表数据使用虚拟滚动**
+3. **避免不必要的重渲染**
+4. **使用React.memo优化组件性能**
+
+### 11.3 代码质量
+
+1. **遵循项目编码标准**
+2. **编写单元测试和集成测试**
+3. **使用TypeScript严格模式**
+4. **运行pnpm typecheck确保类型正确**
+5. **使用ESLint进行代码检查**
+
+## 参考实现
+
+### 12.1 用人方小程序UI包
+
+- `mini-ui-packages/yongren-dashboard-ui`
+- `mini-ui-packages/yongren-order-management-ui`
+- `mini-ui-packages/yongren-talent-management-ui`
+
+### 12.2 人才小程序UI包
+
+- `mini-ui-packages/rencai-dashboard-ui`
+- `mini-ui-packages/rencai-personal-info-ui`
+- `mini-ui-packages/rencai-auth-ui`
+
+### 12.3 共享组件
+
+- `mini-ui-packages/mini-shared-ui-components`
+
+## 版本历史
+
+| 版本 | 日期 | 变更说明 | 作者 |
+|------|------|----------|------|
+| 1.0 | 2025-12-26 | 初始版本,基于史诗011和017经验总结 | Bob (Scrum Master) |
+
+---
+
+**重要提醒**:
+1. 本规范专门针对Taro小程序UI包开发,与Web UI包开发规范(`ui-package-standards.md`)不同
+2. `flex flex-col`是Taro小程序中最常用的布局类,请务必牢记
+3. 所有Mini UI包的开发都应遵循本规范

+ 901 - 0
docs/stories/017.003.story.md

@@ -0,0 +1,901 @@
+# 故事017.003: 个人信息功能实现
+
+## 元信息
+- **史诗**: 017 - 人才小程序功能实现
+- **优先级**: P1 - 核心功能
+- **状态**: Draft
+- **创建日期**: 2025-12-26
+- **负责人**: 开发团队
+
+## 故事描述
+
+**作为** 人才小程序开发者,
+**我想要** 实现个人信息页面功能,
+**以便** 人才用户能够查看个人基本信息、银行卡信息和证件照片。
+
+### 背景
+
+**现有系统状态:**
+- 故事017.001已完成rencai mini ui包基础框架搭建
+- 故事017.002已完成登录与首页实现
+- rencai系列7个UI包基础目录结构已创建
+- `@d8d/rencai-personal-info-ui`包基础框架已就绪
+- API客户端文件已创建(`src/api/index.ts`)
+- mini-talent项目路由结构已配置完成(`pages/personal-info/index`在TabBar中)
+
+**原型设计参考:**
+- `docs/小程序原型/rencai.html` 提供了个人信息页面的完整原型设计
+- 个人信息页 (原型行483-628): 个人基本信息、银行卡信息、证件照片
+
+**技术集成模式:**
+- 参照`yongren-personal-info-ui`的实现模式(如果存在)
+- 个人信息页使用`@d8d/rencai-personal-info-ui`包,集成个人信息查询API、银行卡信息查询API、证件照片查询API
+- API调用逻辑封装在页面组件内部
+- TabBar页面使用Navbar无返回按钮(参照故事017.012规范)
+
+**依赖API (史诗015):**
+- ✅ 个人信息查询API (GET /api/v1/rencai/personal/info) - 已就绪
+- ✅ 银行卡信息查询API (GET /api/v1/rencai/personal/bank-cards) - 已就绪
+- ✅ 证件照片查询API (GET /api/v1/rencai/personal/documents) - 已就绪
+
+**依赖故事完成状态:**
+- ✅ 故事017.001: rencai mini ui包基础框架搭建完成
+- ✅ 故事017.002: 登录与首页实现完成
+- ✅ 故事017.012: 统一Navbar导航栏组件规范完成
+
+## 验收标准
+
+### 个人基本信息展示
+- [ ] 个人信息页面展示完整的个人基本信息(姓名、性别、年龄、身份证号、残疾证号、残疾类型、联系电话、联系地址)
+
+### 银行卡信息展示
+- [ ] 银行卡信息查询接口返回银行卡信息列表
+- [ ] 银行卡信息展示包含:银行名称、卡号脱敏、卡类型、是否默认
+- [ ] 支持多张银行卡的列表展示
+
+### 证件照片展示与预览
+- [ ] 证件照片查询接口返回证件照片信息列表
+- [ ] 证件照片类型包含:身份证、残疾证、体检报告、征信报告
+- [ ] 支持证件照片预览功能(点击照片放大查看)
+
+### 页面设计与布局
+- [ ] 页面设计符合原型标准,移动端体验良好
+- [ ] 已集成Navbar导航栏组件(TabBar页面,无返回按钮)
+- [ ] TabBarLayout底部导航正常工作
+
+### 数据安全与权限
+- [ ] 所有查询接口验证用户权限,确保用户只能查询自己的数据
+- [ ] 银行卡号进行脱敏处理(如:**** **** **** 1234)
+
+### 集成与兼容性
+- [ ] 与前置故事(017.001、017.002)无缝集成
+- [ ] 现有mini-talent项目功能不受影响
+
+## 任务列表
+
+### 任务1: 创建个人信息页面组件 (AC: 个人基本信息展示)
+- [ ] 1.1 在`@d8d/rencai-personal-info-ui`中实现`PersonalInfoPage`页面组件 (`src/pages/PersonalInfoPage/PersonalInfoPage.tsx`)
+- [ ] 1.2 创建个人基本信息卡片组件:
+  - 显示姓名
+  - 显示性别
+  - 显示年龄
+  - 显示身份证号(脱敏显示)
+  - 显示残疾证号(脱敏显示)
+  - 显示残疾类型
+  - 显示联系电话
+  - 显示联系地址
+- [ ] 1.3 集成个人信息查询API (`talentPersonalInfoClient.personal.info.$get`)
+- [ ] 1.4 实现数据加载状态(Loading状态)
+- [ ] 1.5 实现错误处理(API调用失败时显示错误提示)
+
+### 任务2: 实现银行卡信息模块 (AC: 银行卡信息展示)
+- [ ] 2.1 创建银行卡信息卡片组件 (`src/components/BankCardInfo.tsx`)
+- [ ] 2.2 创建银行卡列表项组件 (`src/components/BankCardItem.tsx`)
+- [ ] 2.3 集成银行卡信息查询API (`talentPersonalInfoClient.personal.bankCards.$get`)
+- [ ] 2.4 实现银行卡号脱敏显示(如:**** **** **** 1234)
+- [ ] 2.5 显示银行卡信息:银行名称、卡号脱敏、卡类型(储蓄卡/信用卡)、是否默认标识
+- [ ] 2.6 支持多张银行卡的列表展示
+
+### 任务3: 实现证件照片模块 (AC: 证件照片展示与预览)
+- [ ] 3.1 创建证件照片卡片组件 (`src/components/DocumentPhotos.tsx`)
+- [ ] 3.2 创建证件照片列表项组件 (`src/components/DocumentPhotoItem.tsx`)
+- [ ] 3.3 集成证件照片查询API (`talentPersonalInfoClient.personal.documents.$get`)
+- [ ] 3.4 支持证件照片类型展示:身份证、残疾证、体检报告、征信报告
+- [ ] 3.5 实现证件照片预览功能(点击照片放大查看)
+- [ ] 3.6 使用Taro的Image组件展示照片
+- [ ] 3.7 实现照片预览Modal(使用Taro.previewImage或自定义Modal)
+
+### 任务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/personal-info/index.tsx`:
+  - 从`@d8d/rencai-personal-info-ui/pages/PersonalInfoPage/PersonalInfoPage`导入PersonalInfoPage组件
+  - 用AuthProvider包装页面
+  - 添加认证检查(未登录跳转到登录页)
+  - 导出PersonalInfoPage组件
+- [ ] 5.2 验证页面路由配置(已在故事017.001中配置完成)
+- [ ] 5.3 验证底部TabBar导航正常工作(TabBar中"我的"对应personal-info页面)
+
+### 任务6: 实现页面样式和移动端适配 (AC: 页面设计与布局)
+- [ ] 6.1 参照原型设计实现个人信息页样式(原型行483-628)
+  - 个人基本信息卡片样式(白色卡片,圆角)
+  - 银行卡信息列表样式(白色卡片,列表项分隔)
+  - 证件照片列表样式(白色卡片,网格布局)
+- [ ] 6.2 确保页面设计符合移动端规范:
+  - 宽度参考: 375px
+  - 圆角规范: 12px (卡片)
+  - 颜色主题: 蓝色渐变 (#3b82f6 → #1e40af)
+  - 字体规范: 标题18-24px, 正文14px, 小字12px
+- [ ] 6.3 使用正确的组件:个人信息页使用TabBarLayout(无返回按钮的Navbar)
+
+### 任务7: 编写测试 (AC: 集成与兼容性)
+- [ ] 7.1 为PersonalInfoPage编写组件测试 (`tests/pages/PersonalInfoPage/PersonalInfoPage.test.tsx`)
+  - 测试组件渲染
+  - 测试个人基本信息展示(Mock API响应)
+  - 测试银行卡信息展示(Mock API响应)
+  - 测试证件照片展示(Mock API响应)
+  - 测试照片预览功能
+- [ ] 7.2 编写集成测试验证现有功能不受影响
+- [ ] 7.3 运行`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.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)
+
+### 技术栈要求
+
+**来源**: [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 { talentPersonalInfoClient } from '../../api'
+import { BankCardItem } from '../components/BankCardItem'
+```
+
+**错误示例**:
+```typescript
+// ❌ 错误: 不要使用别名导入UI包内部的模块
+import { talentPersonalInfoClient } from '@/api'
+import { BankCardItem } from '@/components/BankCardItem'
+```
+
+#### 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/PersonalInfoPage/PersonalInfoPage": {
+      "types": "./dist/src/pages/PersonalInfoPage/PersonalInfoPage.d.ts",
+      "import": "./dist/src/pages/PersonalInfoPage/PersonalInfoPage.js",
+      "require": "./dist/src/pages/PersonalInfoPage/PersonalInfoPage.js"
+    }
+  }
+}
+```
+
+#### 3. RPC客户端实现规范
+使用简单的RPC客户端导出模式 (参照`yongren-statistics-ui/src/api/enterpriseStatisticsClient.ts`):
+
+```typescript
+// src/api/talentPersonalInfoClient.ts
+import type { rencaiPersonalInfoRoutes } from '@d8d/core-module/allin-disability-module'
+import { rpcClient } from '@d8d/mini-shared-ui-components/utils/rpc/rpc-client'
+
+// 人才小程序API路径前缀: /api/v1/rencai
+export const talentPersonalInfoClient = rpcClient<typeof rencaiPersonalInfoRoutes>('/api/v1/rencai')
+```
+
+**src/api/index.ts导出**:
+```typescript
+export { talentPersonalInfoClient } from './talentPersonalInfoClient'
+```
+
+#### 4. 类型定义规范
+**必须使用RPC推断类型**,避免直接导入schema类型:
+
+```typescript
+// ✅ 正确:使用RPC推断类型 + 相对路径导入
+import type { InferResponseType } from 'hono'
+import { talentPersonalInfoClient } from '../api/talentPersonalInfoClient'
+
+export type PersonalInfoResponse = InferResponseType<typeof talentPersonalInfoClient.personal.info.$get, 200>
+export type BankCardsResponse = InferResponseType<typeof talentPersonalInfoClient.personal.bankCards.$get, 200>
+export type DocumentsResponse = InferResponseType<typeof talentPersonalInfoClient.personal.documents.$get, 200>
+
+// ❌ 错误:直接导入schema类型 (可能导致Date/string不匹配)
+import type { PersonalInfoSchema } from '@d8d/allin-disability-module/schemas'
+```
+
+#### 5. Jest配置规范
+每个UI包必须创建`jest.config.cjs`配置文件,参照`yongren-dashboard-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}`两种测试文件格式
+
+### 史诗015 API集成要点
+
+**来源**: [docs/prd/epic-015-talent-mini-program-api-support.md](../prd/epic-015-talent-mini-program-api-support.md)
+
+**API路径约定:**
+所有人才小程序API使用 `api/v1/rencai` 前缀:
+- 人才个人信息: `GET /api/v1/rencai/personal/info`
+- 人才银行卡信息: `GET /api/v1/rencai/personal/bank-cards`
+- 人才证件照片: `GET /api/v1/rencai/personal/documents`
+
+**已完成的API (可直接集成):**
+- ✅ 个人信息查询API (故事015-03)
+  - 路由定义: `@d8d/allin-disability-module/src/routes/talent-personal-info.route.ts`
+  - RPC路由: `talentPersonalInfoRoutes.personal.info`
+  - 响应格式: `{ name: string, gender: string, age: number, idCard: string, disabilityCard: string, disabilityType: string, phone: string, address: string, ... }`
+- ✅ 银行卡信息查询API (故事015-04)
+  - 路由定义: `@d8d/allin-disability-module/src/routes/talent-bank-cards.route.ts`
+  - RPC路由: `talentPersonalInfoRoutes.personal.bankCards`
+  - 响应格式: `{ bankCards: [{ bankName: string, cardNumber: string, cardType: string, isDefault: boolean, ... }] }`
+- ✅ 证件照片查询API (故事015-05)
+  - 路由定义: `@d8d/allin-disability-module/src/routes/talent-documents.route.ts`
+  - RPC路由: `talentPersonalInfoRoutes.personal.documents`
+  - 响应格式: `{ documents: [{ type: string, url: string, uploadDate: Date, ... }] }`
+
+**只读设计原则:**
+遵循与用人方小程序相同的设计原则,人才小程序API以查询功能为主。
+
+### 原型设计参考
+
+**来源**: [docs/小程序原型/rencai.html](../小程序原型/rencai.html)
+
+**个人信息页** (原型行483-628):
+```html
+<!-- 个人基本信息卡片 -->
+<div style="background: white; border-radius: 12px;">
+  <h3>个人基本信息</h3>
+  <div>姓名: 张三</div>
+  <div>性别: 男</div>
+  <div>年龄: 35</div>
+  <div>身份证号: 3301**********1234</div>
+  <div>残疾证号: 3301**********5678</div>
+  <div>残疾类型: 肢体残疾</div>
+  <div>联系电话: 138****5678</div>
+  <div>联系地址: 浙江省杭州市...</div>
+</div>
+
+<!-- 银行卡信息卡片 -->
+<div style="background: white; border-radius: 12px;">
+  <h3>银行卡信息</h3>
+  <div style="border-bottom: 1px solid #eee;">
+    <div>中国建设银行</div>
+    <div>**** **** **** 1234</div>
+    <div>储蓄卡</div>
+    <div>默认</div>
+  </div>
+  <div>
+    <div>中国工商银行</div>
+    <div>**** **** **** 5678</div>
+    <div>储蓄卡</div>
+  </div>
+</div>
+
+<!-- 证件照片卡片 -->
+<div style="background: white; border-radius: 12px;">
+  <h3>证件照片</h3>
+  <div style="display: grid; grid-template-columns: 1fr 1fr;">
+    <div>
+      <img src="id-card-front.jpg" />
+      <div>身份证(正面)</div>
+    </div>
+    <div>
+      <img src="id-card-back.jpg" />
+      <div>身份证(反面)</div>
+    </div>
+    <div>
+      <img src="disability-card.jpg" />
+      <div>残疾证</div>
+    </div>
+    <div>
+      <img src="medical-report.jpg" />
+      <div>体检报告</div>
+    </div>
+  </div>
+</div>
+```
+
+**移动端设计规范:**
+- 宽度参考: 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 PersonalInfoPage() {
+  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
+│   │   ├── personal-info/     # 个人信息页 (从UI包导入) - 本故事
+│   │   │   └── index.tsx
+│   │   ├── attendance/        # 考勤记录页 (从UI包导入) - 故事017.004
+│   │   │   └── index.tsx
+│   │   └── settings/          # 设置页 (从UI包导入) - 故事017.006
+│   │       └── index.tsx
+├── package.json
+├── jest.config.js             # Jest配置
+└── tsconfig.json
+```
+
+**mini-ui-packages目录结构:**
+```
+mini-ui-packages/
+├── rencai-personal-info-ui/   # 人才个人信息UI包
+│   ├── src/
+│   │   ├── api/
+│   │   │   ├── talentPersonalInfoClient.ts
+│   │   │   └── index.ts
+│   │   ├── pages/
+│   │   │   └── PersonalInfoPage/
+│   │   │       ├── PersonalInfoPage.tsx
+│   │   │       └── index.ts (可选)
+│   │   ├── components/        # UI组件
+│   │   │   ├── PersonalBasicInfo.tsx    # 个人基本信息卡片
+│   │   │   ├── BankCardInfo.tsx         # 银行卡信息卡片
+│   │   │   ├── BankCardItem.tsx         # 银行卡列表项
+│   │   │   ├── DocumentPhotos.tsx       # 证件照片卡片
+│   │   │   └── DocumentPhotoItem.tsx    # 证件照片列表项
+│   │   └── 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/personal-info/index.tsx
+import PersonalInfoPage from '@d8d/rencai-personal-info-ui/pages/PersonalInfoPage/PersonalInfoPage'
+import { AuthContextProvider, useAuth } from '@d8d/rencai-auth-ui/utils'
+
+function PersonalInfo() {
+  const { isLoggedIn } = useAuth()
+
+  // 未登录跳转到登录页
+  if (!isLoggedIn) {
+    Taro.navigateTo({ url: '/pages/login/index' })
+    return null
+  }
+
+  return <PersonalInfoPage />
+}
+
+export default function PersonalInfoIndex() {
+  return (
+    <AuthContextProvider>
+      <PersonalInfo />
+    </AuthContextProvider>
+  )
+}
+```
+
+### 测试策略
+
+**来源**: [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/
+    │       ├── PersonalBasicInfo.test.tsx
+    │       ├── BankCardItem.test.tsx
+    │       └── DocumentPhotoItem.test.tsx
+    └── pages/                     # 页面组件测试
+        └── PersonalInfoPage/
+            └── PersonalInfoPage.test.tsx
+```
+
+**测试要求:**
+1. 为每个页面组件编写Jest测试
+2. 测试API客户端的集成(使用Mock响应)
+3. 测试银行卡号脱敏逻辑
+4. 测试证件照片预览功能
+5. 验证mini-talent项目现有功能不受影响
+6. 运行`pnpm typecheck`确保类型检查通过
+
+**Mock响应工具函数:**
+```typescript
+const createMockResponse = (status: number, data?: any) => ({
+  status,
+  ok: status >= 200 && status < 300,
+  body: null,
+  bodyUsed: false,
+  statusText: status === 200 ? 'OK' : 'Error',
+  headers: new Headers(),
+  url: '',
+  redirected: false,
+  type: 'basic' as ResponseType,
+  json: async () => data || {},
+  text: async () => '',
+  blob: async () => new Blob(),
+  arrayBuffer: async () => new ArrayBuffer(0),
+  formData: async () => new FormData(),
+  clone: function() { return this; }
+})
+```
+
+### 编码标准
+
+**来源**: [architecture/coding-standards.md](../architecture/coding-standards.md)
+
+**关键编码规范:**
+
+#### 1. 必须遵循UI包开发规范
+开发UI包时,**必须**参考并遵循[UI包开发规范](../architecture/ui-package-standards.md),该规范基于史诗008(AllIn UI模块移植)的经验总结。
+
+#### 2. 关键检查点 (基于史诗008经验)
+- **API路径映射验证**: 开发前必须验证故事中的API路径映射与实际后端路由定义的一致性
+- **类型推断最佳实践**: 必须使用RPC推断类型,而不是直接导入schema类型
+- **测试选择器优化**: 必须为关键交互元素添加`data-testid`属性
+- **API调用一致性**: 必须根据实际路由名称修正API调用
+
+#### 3. 常见错误避免
+- ❌ 不要直接导入schema类型 (可能导致Date/string类型不匹配)
+- ❌ 不要使用`getByText()`查找可能重复的文本元素
+- ❌ 不要在UI包内部导入中使用别名 (`@/`, `~/`等),必须使用相对路径
+- ❌ 不要忘记在TabBar页面集成Navbar导航栏组件
+- ❌ 不要在TabBar页面添加返回按钮
+
+**路径使用示例**:
+```typescript
+// ✅ 正确: UI包内部使用相对路径
+import { apiClient } from '../../api'
+import { MyComponent } from '../components'
+
+// ✅ 正确: 跨包导入使用workspace包名
+import { SharedComponent } from '@d8d/mini-shared-ui-components'
+
+// ❌ 错误: UI包内部使用别名
+import { apiClient } from '@/api'
+import { MyComponent } from '@/pages/PersonalInfoPage/components'
+```
+
+#### 4. 参考实现
+- **用人方个人信息UI包**: `mini-ui-packages/yongren-personal-info-ui` (如果存在)
+- **用人方仪表板UI包**: `mini-ui-packages/yongren-dashboard-ui`
+  - 组件: `src/pages/Dashboard/Dashboard.tsx`
+  - package.json exports配置
+  - 目录结构参考
+  - Jest配置: `jest.config.cjs`
+
+### 数据脱敏规范
+
+**银行卡号脱敏:**
+```typescript
+/**
+ * 脱敏银行卡号
+ * @param cardNumber 完整银行卡号
+ * @returns 脱敏后的银行卡号(如:**** **** **** 1234)
+ */
+export function maskCardNumber(cardNumber: string): string {
+  if (!cardNumber || cardNumber.length < 4) {
+    return '****'
+  }
+  const last4 = cardNumber.slice(-4)
+  return `**** **** **** ${last4}`
+}
+
+// 使用示例
+<BankCardItem>
+  <Text>{maskCardNumber(bankCard.cardNumber)}</Text>
+</BankCardItem>
+```
+
+**身份证号脱敏:**
+```typescript
+/**
+ * 脱敏身份证号
+ * @param idCard 完整身份证号
+ * @returns 脱敏后的身份证号(如:3301**********1234)
+ */
+export function maskIdCard(idCard: string): string {
+  if (!idCard || idCard.length < 8) {
+    return '********'
+  }
+  const prefix = idCard.slice(0, 4)
+  const suffix = idCard.slice(-4)
+  return `${prefix}**********${suffix}`
+}
+
+// 使用示例
+<PersonalBasicInfo>
+  <Text>身份证号: {maskIdCard(personalInfo.idCard)}</Text>
+</PersonalBasicInfo>
+```
+
+### 证件照片预览实现
+
+**使用Taro.previewImage:**
+```typescript
+import Taro from '@tarojs/taro'
+import { Image, Text } from '@tarojs/components'
+
+interface DocumentPhotoItemProps {
+  type: string
+  url: string
+}
+
+export function DocumentPhotoItem({ type, url }: DocumentPhotoItemProps) {
+  const handlePreview = () => {
+    Taro.previewImage({
+      current: url, // 当前显示图片的http链接
+      urls: [url] // 需要预览的图片http链接列表
+    })
+  }
+
+  return (
+    <View onClick={handlePreview} className="flex flex-col">
+      <Image
+        src={url}
+        mode="aspectFill"
+        className="w-full h-32 rounded-lg"
+      />
+      <Text>{type}</Text>
+    </View>
+  )
+}
+```
+
+### 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 PersonalBasicInfo({ personalInfo }: { personalInfo: PersonalInfoResponse }) {
+  return (
+    <View className="bg-white rounded-lg p-4">
+      <Text className="text-lg font-semibold mb-4">个人基本信息</Text>
+
+      {/* 垂直布局的信息列表 */}
+      <View className="flex flex-col space-y-2">
+        <View className="flex justify-between">
+          <Text className="text-gray-600">姓名</Text>
+          <Text>{personalInfo.name}</Text>
+        </View>
+
+        <View className="flex justify-between">
+          <Text className="text-gray-600">性别</Text>
+          <Text>{personalInfo.gender}</Text>
+        </View>
+
+        <View className="flex justify-between">
+          <Text className="text-gray-600">年龄</Text>
+          <Text>{personalInfo.age}</Text>
+        </View>
+
+        {/* 更多字段... */}
+      </View>
+    </View>
+  )
+}
+```
+
+**关键点**:
+1. 信息列表容器使用 `flex flex-col` 实现垂直布局
+2. 每个信息项使用 `flex justify-between` 实现标签和值的左右分布
+3. 使用 `space-y-2` 或 `space-y-3` 添加垂直间距
+4. **重要**: 记住在所有需要垂直排列的 View 上添加 `flex flex-col`
+
+### 技术约束
+
+1. **向后兼容**: 不影响现有mini-talent项目功能
+2. **类型安全**: 使用TypeScript严格模式,所有API调用必须有类型定义
+3. **模块独立性**: 每个UI包独立管理自己的API客户端和类型定义
+4. **测试覆盖**: 所有新增代码必须有测试覆盖
+5. **代码规范**: 遵循项目编码标准和UI包开发规范
+6. **数据安全**: 银行卡号、身份证号等敏感信息必须脱敏显示
+
+### 风险和缓解措施
+
+**主要风险:**
+1. **API集成风险**: 史诗015的API可能存在接口变更
+2. **数据脱敏风险**: 银行卡号、身份证号脱敏逻辑可能存在漏洞
+3. **照片预览风险**: Taro.previewImage在不同平台可能有兼容性问题
+4. **UI组件复用风险**: rencai系列UI包可能与现有yongren系列UI包存在差异
+
+**缓解措施:**
+1. **API验证**: 开发前验证API路径映射与实际后端路由定义的一致性
+2. **分阶段实现**: 先实现个人基本信息,再实现银行卡信息,最后实现证件照片
+3. **参考现有模式**: 参照yongren系列UI包的实现模式和架构
+4. **类型安全**: 使用RPC推断类型,避免类型不匹配问题
+5. **测试驱动**: 编写完整的测试,确保功能正确性
+6. **数据安全测试**: 测试各种边界情况,确保脱敏逻辑正确
+
+## 测试
+
+### 测试框架和模式
+
+**来源**: [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/
+    │       ├── PersonalBasicInfo.test.tsx
+    │       ├── BankCardItem.test.tsx
+    │       └── DocumentPhotoItem.test.tsx
+    └── pages/                     # 页面组件测试
+        └── PersonalInfoPage/
+            └── PersonalInfoPage.test.tsx
+```
+
+### 测试要求
+
+1. **组件测试**:
+   - 测试组件渲染正确
+   - 测试用户交互(点击照片预览等)
+   - 测试API集成(使用Mock响应)
+   - 测试错误处理
+
+2. **脱敏逻辑测试**:
+   - 测试银行卡号脱敏功能
+   - 测试身份证号脱敏功能
+   - 测试各种边界情况(空值、短字符串等)
+
+3. **集成测试**:
+   - 测试个人信息页面的三个模块数据加载
+   - 测试Navbar导航栏的正确显示
+
+4. **回归测试**:
+   - 验证mini-talent项目现有功能不受影响
+   - 运行`pnpm typecheck`确保类型检查通过
+
+### 测试执行
+
+```bash
+# 运行所有测试
+cd mini-ui-packages/rencai-personal-info-ui && pnpm test
+
+# 运行特定测试
+pnpm test --testNamePattern="PersonalInfoPage"
+
+# 生成覆盖率报告
+pnpm test:coverage
+```
+
+## 变更日志
+
+| 日期 | 版本 | 描述 | 作者 |
+|------|------|------|------|
+| 2025-12-26 | 1.0 | 创建故事文档 | Bob (Scrum Master) |
+
+## 开发者记录
+
+*此部分由开发代理在实施过程中填写*
+
+### 使用的代理模型
+
+待填写
+
+### 调试日志引用
+
+待填写
+
+### 完成说明列表
+
+待填写
+
+### 文件列表
+
+待填写
+
+## QA结果
+
+*此部分由QA代理在审查完成后填写*