Ver Fonte

✨ feat(mobile): 实现银龄岗位页面及相关组件

- 创建银龄智慧移动端UI设计规范文档,定义水墨风格色彩系统、字体系统和组件规范
- 实现JobCard组件,展示岗位信息,符合水墨风格设计规范
- 开发useJobs hook,提供岗位数据获取和格式化功能
- 重构SilverJobsPage页面,实现完整的岗位列表展示功能
- 添加色彩系统和字体系统样式文件,统一UI风格
- 实现岗位筛选功能,支持按岗位类型过滤
- 添加加载状态、错误状态和空状态处理

📝 docs(mobile): 添加移动端UI设计规范文档

- 定义中国水墨风格的色彩系统,包括主色调、语义色彩和文字色彩
- 建立字体层级和规范,针对银龄用户优化可读性
- 制定布局规范、组件规范和交互规范
- 添加响应式设计和可访问性规范,确保适老化体验
yourname há 7 meses atrás
pai
commit
65f3d3dc9b

+ 432 - 0
docs/mobile-ui-design-guidelines.md

@@ -0,0 +1,432 @@
+# 银龄智慧移动端UI设计规范
+
+## 概述
+
+本文档定义了银龄智慧移动端应用的UI设计规范,基于中国水墨风格设计理念,确保所有移动端页面保持一致的视觉风格和用户体验。
+
+## 设计原则
+
+1. **文化性**:体现中国传统文化美学,以水墨风格为核心
+2. **适老性**:专为银龄用户群体优化,保证良好的可访问性
+3. **简洁性**:界面简洁直观,减少认知负担
+4. **一致性**:所有页面遵循统一的设计语言和交互模式
+
+## 色彩系统
+
+### 主色调 - 中国水墨
+
+```css
+/* 水墨色彩系统 */
+--ink-light: #f5f3f0;     /* 宣纸背景色 */
+--ink-medium: #d4c4a8;    /* 淡墨 */
+--ink-dark: #8b7355;      /* 浓墨 */
+--ink-deep: #3a2f26;      /* 焦墨 */
+
+/* 点缀色彩 */
+--accent-red: #a85c5c;    /* 朱砂 */
+--accent-blue: #4a6b7c;   /* 花青 */
+--accent-green: #5c7c5c;  /* 石绿 */
+
+/* 文字色彩 */
+--text-primary: #2f1f0f;   /* 墨色文字 */
+--text-secondary: #5d4e3b; /* 淡墨文字 */
+--text-light: #8b7355;     /* 极淡文字 */
+```
+
+### 语义色彩
+
+| 场景 | 颜色值 | 说明 |
+|------|--------|------|
+| 成功 | #5c7c5c | 石绿色,用于正向反馈 |
+| 警告 | #a85c5c | 朱砂色,用于警示信息 |
+| 错误 | #a85c5c | 朱砂色,用于错误提示 |
+| 信息 | #4a6b7c | 花青色,用于信息提示 |
+
+## 字体系统
+
+### 字体层级
+
+```typescript
+// 字体样式定义
+const FONT_STYLES = {
+  title: 'font-serif text-3xl font-bold tracking-wide',        // 页面标题
+  sectionTitle: 'font-serif text-2xl font-semibold tracking-wide', // 区块标题
+  body: 'font-sans text-base leading-relaxed',                 // 正文文本
+  caption: 'font-sans text-sm',                               // 说明文字
+  small: 'font-sans text-xs',                                 // 辅助文字
+};
+```
+
+### 字体规范
+
+| 类型 | 字体 | 大小 | 行高 | 字重 | 用途 |
+|------|------|------|------|------|------|
+| 标题 | serif | 28px | 1.2 | bold | 页面主标题 |
+| 区块标题 | serif | 24px | 1.2 | semibold | 内容区块标题 |
+| 正文 | sans | 16px | 1.6 | normal | 主要内容文字 |
+| 说明 | sans | 14px | 1.4 | normal | 辅助说明文字 |
+| 标签 | sans | 12px | 1.3 | normal | 标签、提示文字 |
+
+## 布局规范
+
+### 基础布局结构
+
+```css
+/* 移动端标准布局 */
+.container {
+  min-height: 100vh;
+  background-color: var(--ink-light);
+  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
+}
+
+.content-area {
+  padding: 16px;
+  margin-bottom: 64px; /* 底部标签栏高度 */
+}
+```
+
+### 间距系统
+
+| 间距 | 数值 | 用途 |
+|------|------|------|
+| xs | 4px | 图标与文字间距 |
+| sm | 8px | 组件内部间距 |
+| md | 16px | 组件间间距 |
+| lg | 24px | 区块间间距 |
+| xl | 32px | 页面区块间距 |
+
+## 组件规范
+
+### 1. 头部导航栏
+
+```typescript
+// 标准头部导航栏
+<header className="shadow-sm sticky top-0 z-10 border-b border-opacity-20"
+  style={{ backgroundColor: 'var(--ink-light)', borderColor: 'var(--ink-medium)' }}>
+</header>
+```
+
+**规范说明**:
+- 高度:自适应内容,建议auto
+- 背景:宣纸色(var(--ink-light))
+- 边框:淡墨色(var(--ink-medium)),透明度20%
+- 阴影:轻微阴影,保持水墨质感
+- 定位:sticky,顶部固定
+
+### 2. 卡片组件
+
+```typescript
+// 标准卡片
+<div className="rounded-xl p-4 shadow-sm hover:shadow-lg transition-all duration-300 backdrop-blur-sm"
+  style={{ 
+    backgroundColor: 'rgba(255,255,255,0.8)',
+    border: `1px solid var(--ink-medium)`
+  }}>
+</div>
+```
+
+**卡片变体**:
+- 普通卡片:rgba(255,255,255,0.8)
+- 高亮卡片:rgba(255,255,255,0.9)
+- 透明卡片:rgba(255,255,255,0.6)
+
+### 3. 按钮组件
+
+#### 主要按钮
+```typescript
+<button className="px-4 py-2 rounded-full transition-all duration-300 hover:shadow-md"
+  style={{
+    backgroundColor: 'var(--ink-dark)',
+    color: 'white',
+    borderColor: 'var(--ink-medium)'
+  }}>
+</button>
+```
+
+#### 次要按钮
+```typescript
+<button className="px-4 py-2 rounded-full border transition-all duration-300 hover:shadow-md"
+  style={{
+    color: 'var(--text-primary)',
+    borderColor: 'var(--ink-medium)',
+    backgroundColor: 'transparent'
+  }}>
+</button>
+```
+
+### 4. 输入框组件
+
+```typescript
+<div className="flex items-center rounded-full px-4 py-3 shadow-sm border transition-all duration-300 focus-within:shadow-md"
+  style={{ 
+    backgroundColor: 'rgba(255,255,255,0.7)',
+    borderColor: 'var(--ink-medium)' 
+  }}>
+</div>
+```
+
+### 5. 标签组件
+
+```typescript
+<span className="px-2 py-1 rounded-full text-xs"
+  style={{
+    backgroundColor: 'var(--accent-blue)',
+    color: 'white'
+  }}>
+</span>
+```
+
+## 列表规范
+
+### 1. 单列列表项
+
+```typescript
+<div className="flex items-center p-4 rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer backdrop-blur-sm"
+  style={{
+    backgroundColor: 'rgba(255,255,255,0.8)',
+    border: `1px solid var(--ink-medium)`
+  }}>
+  {/* 左侧图片/图标 */}
+  <img className="w-14 h-14 rounded-full object-cover mr-4 border-2" 
+    style={{ borderColor: 'var(--ink-medium)' }} />
+  
+  {/* 中间内容 */}
+  <div className="flex-1">
+    <h4 className="font-medium" style={{ color: 'var(--text-primary)' }}>标题</h4>
+    <p className="text-sm" style={{ color: 'var(--text-secondary)' }}>副标题</p>
+  </div>
+  
+  {/* 右侧操作 */}
+  <div className="text-right">
+    <span className="text-xs">附加信息</span>
+  </div>
+</div>
+```
+
+### 2. 网格列表项
+
+```typescript
+<div className="grid grid-cols-2 gap-3">
+  <div className="rounded-xl p-3 cursor-pointer transition-all duration-300 hover:shadow-lg backdrop-blur-sm"
+    style={{
+      backgroundColor: 'rgba(255,255,255,0.8)',
+      border: `1px solid var(--ink-medium)'
+    }}>
+  </div>
+</div>
+```
+
+## 底部标签栏
+
+```typescript
+// 底部标签栏容器
+<footer className="bg-white border-t border-gray-200 py-2">
+  <div className="grid grid-cols-5 gap-2 text-center">
+    <button className="flex flex-col items-center py-1 text-orange-500">
+      <span className="text-xl mb-1">🏠</span>
+      <span className="text-xs font-medium">首页</span>
+    </button>
+  </div>
+</footer>
+```
+
+## 动效规范
+
+### 过渡动画
+
+| 类型 | 时长 | 缓动函数 | 用途 |
+|------|------|----------|------|
+| 快速 | 200ms | ease-out | 按钮悬停、标签切换 |
+| 标准 | 300ms | ease-in-out | 页面切换、卡片展开 |
+| 缓慢 | 500ms | ease-in-out | 模态框、抽屉动画 |
+
+### 阴影动效
+
+```css
+/* 基础阴影 */
+.shadow-sm {
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+/* 悬停阴影 */
+.hover\:shadow-md:hover {
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
+}
+
+/* 点击阴影 */
+.active\:shadow-lg:active {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
+}
+```
+
+## 响应式设计
+
+### 断点系统
+
+| 断点 | 宽度 | 描述 |
+|------|------|------|
+| xs | < 375px | 小屏手机 |
+| sm | ≥ 375px | 标准手机 |
+| md | ≥ 768px | 平板竖屏 |
+| lg | ≥ 1024px | 平板横屏 |
+
+### 适配规范
+
+1. **小屏适配**:最小宽度320px,保证核心功能可用
+2. **大屏优化**:最大宽度414px,居中显示,两侧留白
+3. **字体适配**:基础字体16px,支持系统字体缩放
+4. **触摸目标**:最小44×44px,符合iOS人机界面指南
+
+## 可访问性规范
+
+### 颜色对比度
+
+| 文字颜色 | 背景颜色 | 对比度 | 是否符合WCAG 2.1 |
+|----------|----------|--------|------------------|
+| #2f1f0f | #f5f3f0 | 8.2:1 | AAA级 |
+| #5d4e3b | #f5f3f0 | 5.9:1 | AA级 |
+| #8b7355 | #f5f3f0 | 3.9:1 | AA级(大字体) |
+
+### 无障碍设计
+
+1. **语义标签**:使用正确的HTML语义标签
+2. **键盘导航**:支持Tab键导航,焦点样式清晰
+3. **屏幕阅读器**:提供aria-label和aria-describedby属性
+4. **颜色无依赖**:重要信息不仅通过颜色传达
+5. **文字缩放**:支持系统文字大小调整
+
+## 图标规范
+
+### 图标使用
+
+```typescript
+// Heroicons 使用示例
+import { BriefcaseIcon } from '@heroicons/react/24/outline';
+
+<BriefcaseIcon className="w-6 h-6" style={{ color: 'var(--accent-blue)' }} />
+```
+
+### 图标尺寸
+
+| 用途 | 尺寸 | 示例 |
+|------|------|------|
+| 导航图标 | 24×24px | 底部标签栏 |
+| 功能图标 | 20×20px | 卡片内图标 |
+| 装饰图标 | 16×16px | 标签内小图标 |
+| 大图标 | 32×32px | 空状态图标 |
+
+## 图片规范
+
+### 图片比例
+
+| 类型 | 比例 | 用途 |
+|------|------|------|
+| 正方形 | 1:1 | 头像、缩略图 |
+| 横向 | 16:9 | 轮播图、横幅 |
+| 纵向 | 4:3 | 卡片图片 |
+| 圆形 | 1:1 | 用户头像 |
+
+### 图片处理
+
+1. **懒加载**:使用loading="lazy"属性
+2. **占位图**:提供默认占位图片
+3. **WebP格式**:优先使用WebP格式,提供JPG备选
+4. **尺寸优化**:移动端图片宽度不超过750px
+
+## 交互规范
+
+### 手势规范
+
+| 手势 | 操作 | 反馈 |
+|------|------|------|
+| 点击 | 导航、选择 | 背景色变化 |
+| 长按 | 上下文菜单 | 震动反馈 |
+| 滑动 | 页面切换 | 视觉过渡 |
+| 下拉 | 刷新 | 加载动画 |
+
+### 加载状态
+
+1. **骨架屏**:内容加载时的占位效果
+2. **加载动画**:旋转动画或进度条
+3. **错误状态**:友好的错误提示和重试按钮
+4. **空状态**:清晰的空状态说明和引导
+
+## 开发规范
+
+### 文件结构
+
+```
+src/client/mobile/
+├── components/          # 通用组件
+│   ├── [ComponentName].tsx
+│   └── index.ts
+├── pages/              # 页面组件
+│   ├── [PageName].tsx
+│   └── index.ts
+├── layouts/            # 布局组件
+├── hooks/              # 自定义hooks
+├── utils/              # 工具函数
+└── styles/             # 样式文件
+    ├── colors.ts       # 色彩定义
+    ├── fonts.ts        # 字体定义
+    └── animations.ts   # 动画定义
+```
+
+### 命名规范
+
+1. **组件命名**:PascalCase
+2. **文件命名**:kebab-case
+3. **样式类名**:kebab-case
+4. **常量命名**:UPPER_SNAKE_CASE
+5. **变量命名**:camelCase
+
+### 代码风格
+
+```typescript
+// 样式常量
+const COLORS = {
+  ink: {
+    light: '#f5f3f0',
+    medium: '#d4c4a8',
+    dark: '#8b7355',
+    deep: '#3a2f26',
+  },
+  accent: {
+    red: '#a85c5c',
+    blue: '#4a6b7c',
+    green: '#5c7c5c',
+  }
+};
+
+// 字体样式
+const FONT_STYLES = {
+  title: 'font-serif text-3xl font-bold tracking-wide',
+  body: 'font-sans text-base leading-relaxed',
+};
+```
+
+## 测试规范
+
+### 兼容性测试
+
+1. **设备测试**:iPhone SE - iPhone 15 Pro Max
+2. **系统测试**:iOS 14+,Android 8+
+3. **浏览器测试**:Safari,Chrome,微信浏览器
+4. **网络测试**:3G,4G,WiFi环境
+
+### 功能测试
+
+1. **触摸测试**:所有可点击元素测试
+2. **手势测试**:滑动、下拉、长按等
+3. **状态测试**:加载、空状态、错误状态
+4. **性能测试**:滚动流畅度、动画性能
+
+## 更新记录
+
+| 版本 | 日期 | 更新内容 | 更新人 |
+|------|------|----------|--------|
+| 1.0 | 2025-07-21 | 初始版本创建 | 架构师 |
+
+---
+
+本文档将持续更新,如有疑问请联系UI设计团队。

+ 123 - 0
src/client/mobile/components/silver-jobs/JobCard.tsx

@@ -0,0 +1,123 @@
+import React from 'react';
+import { JobItem, formatSalary, formatPublishDate, getJobTypeIcon } from '@/client/mobile/hooks/useJobs';
+import { INK_COLORS } from '@/client/mobile/styles/colors';
+
+interface JobCardProps {
+  job: JobItem;
+  onPress?: () => void;
+}
+
+export const JobCard: React.FC<JobCardProps> = ({ job, onPress }) => {
+  return (
+    <div 
+      className="mb-4 mx-4 rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer backdrop-blur-sm border overflow-hidden"
+      style={{ 
+        backgroundColor: 'rgba(255,255,255,0.8)',
+        borderColor: INK_COLORS.ink.medium
+      }}
+      onClick={onPress}
+    >
+      <div className="p-4">
+        {/* 顶部信息 */}
+        <div className="flex items-start justify-between mb-3">
+          <div className="flex items-center">
+            <div 
+              className="w-14 h-14 rounded-full mr-3 flex items-center justify-center text-lg font-bold border-2"
+              style={{ borderColor: INK_COLORS.ink.medium }}
+            >
+              {job.companyLogo ? (
+                <img 
+                  src={job.companyLogo} 
+                  alt={job.companyName} 
+                  className="w-full h-full rounded-full object-cover"
+                />
+              ) : (
+                <span style={{ color: INK_COLORS.ink.dark }}>
+                  {job.companyName.slice(0, 2)}
+                </span>
+              )}
+            </div>
+            <div>
+              <h3 
+                className="font-serif text-lg font-medium leading-snug"
+                style={{ color: INK_COLORS.text.primary }}
+              >
+                {job.title}
+              </h3>
+              <p 
+                className="font-sans text-sm leading-normal mt-1"
+                style={{ color: INK_COLORS.text.secondary }}
+              >
+                {job.companyName}
+              </p>
+            </div>
+          </div>
+          <span 
+            className="px-2 py-1 rounded-full text-xs font-medium"
+            style={{ backgroundColor: INK_COLORS.accent.blue, color: 'white' }}
+          >
+            {job.jobType}
+          </span>
+        </div>
+
+        {/* 基本信息 */}
+        <div className="flex items-center justify-between mb-2">
+          <div className="flex items-center">
+            <span className="text-sm mr-2">📍</span>
+            <span 
+              className="font-sans text-sm"
+              style={{ color: INK_COLORS.text.secondary }}
+            >
+              {job.location}
+            </span>
+          </div>
+          <span className="text-sm">💰 {formatSalary(job.salaryMin, job.salaryMax)}</span>
+        </div>
+
+        {/* 职位描述 */}
+        <p 
+          className="font-sans text-sm leading-relaxed mb-3 line-clamp-2"
+          style={{ color: INK_COLORS.text.light }}
+        >
+          {job.description}
+        </p>
+
+        {/* 福利标签 */}
+        <div className="flex flex-wrap gap-1 mb-3">
+          {job.benefits.slice(0, 3).map((benefit, index) => (
+            <span
+              key={index}
+              className="px-2 py-1 text-xs rounded-full"
+              style={{ 
+                backgroundColor: INK_COLORS.accent.green + '20',
+                color: INK_COLORS.accent.green,
+                borderColor: INK_COLORS.accent.green + '30'
+              }}
+            >
+              {benefit}
+            </span>
+          ))}
+        </div>
+
+        {/* 底部信息 */}
+        <div className="flex items-center justify-between text-xs pt-3 border-t"
+             style={{ borderColor: INK_COLORS.border.light }}
+        >
+          <div className="flex items-center space-x-3">
+            <span style={{ color: INK_COLORS.text.light }}>
+              👁️ {job.views}
+            </span>
+            <span style={{ color: INK_COLORS.text.light }}>
+              📋 {job.applications}人投递
+            </span>
+          </div>
+          <span style={{ color: INK_COLORS.text.light }}>
+            {formatPublishDate(job.publishDate)}
+          </span>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default JobCard;

+ 167 - 0
src/client/mobile/hooks/useJobs.ts

@@ -0,0 +1,167 @@
+import { useState, useEffect, useCallback } from 'react';
+
+// 岗位数据类型定义
+export interface JobItem {
+  id: number;
+  title: string;
+  companyName: string;
+  location: string;
+  salaryMin: number;
+  salaryMax: number;
+  jobType: string;
+  description: string;
+  requirements: string;
+  benefits: string[];
+  publishDate: string;
+  views: number;
+  applications: number;
+  companyLogo?: string;
+}
+
+// Hook参数类型
+interface UseJobsOptions {
+  page?: number;
+  pageSize?: number;
+  keyword?: string;
+  location?: string;
+  jobType?: string;
+}
+
+interface UseJobsReturn {
+  jobs: JobItem[];
+  loading: boolean;
+  error: string | null;
+  hasMore: boolean;
+  refresh: () => Promise<void>;
+  loadMore: () => Promise<void>;
+}
+
+// 模拟数据
+const mockJobs: JobItem[] = [
+  {
+    id: 1,
+    title: '社区老年活动中心管理员',
+    companyName: '幸福社区服务中心',
+    location: '北京市朝阳区',
+    salaryMin: 3000,
+    salaryMax: 5000,
+    jobType: '全职',
+    description: '负责老年活动中心的日常管理和活动组织',
+    requirements: '有责任心,热爱老年事业',
+    benefits: ['五险一金', '带薪年假', '节日福利'],
+    publishDate: '2024-07-20',
+    views: 156,
+    applications: 23,
+    companyLogo: 'https://via.placeholder.com/60x60.png?text=幸福社区'
+  },
+  {
+    id: 2,
+    title: '图书馆银龄阅读推广员',
+    companyName: '西城区图书馆',
+    location: '北京市西城区',
+    salaryMin: 2500,
+    salaryMax: 4000,
+    jobType: '兼职',
+    description: '推广银龄阅读项目,组织读书会活动',
+    requirements: '热爱阅读,善于沟通',
+    benefits: ['弹性工作', '培训机会'],
+    publishDate: '2024-07-19',
+    views: 89,
+    applications: 12,
+    companyLogo: 'https://via.placeholder.com/60x60.png?text=西城图书馆'
+  },
+  {
+    id: 3,
+    title: '老年大学讲师(书法班)',
+    companyName: '北京市老年大学',
+    location: '北京市海淀区',
+    salaryMin: 4000,
+    salaryMax: 6000,
+    jobType: '兼职',
+    description: '教授书法课程,传承传统文化',
+    requirements: '书法功底深厚,有教学经验',
+    benefits: ['课时费优厚', '教学资源支持'],
+    publishDate: '2024-07-18',
+    views: 234,
+    applications: 45,
+    companyLogo: 'https://via.placeholder.com/60x60.png?text=老年大学'
+  }
+];
+
+export const useJobs = (): UseJobsReturn => {
+  const [jobs, setJobs] = useState<JobItem[]>(mockJobs);
+  const [loading, setLoading] = useState(false);
+  const [error, setError] = useState<string | null>(null);
+  const [hasMore, setHasMore] = useState(false);
+
+  const fetchJobs = useCallback(async () => {
+    setLoading(true);
+    setError(null);
+
+    try {
+      // 模拟API延迟
+      await new Promise(resolve => setTimeout(resolve, 500));
+      setJobs(mockJobs); // 使用模拟数据
+    } catch (err) {
+      setError('获取岗位列表失败');
+    } finally {
+      setLoading(false);
+    }
+  }, []);
+
+  const refresh = async () => {
+    await fetchJobs();
+  };
+
+  const loadMore = async () => {
+    // 这里可以实现加载更多逻辑
+    setHasMore(false);
+  };
+
+  // 初始加载
+  useEffect(() => {
+    fetchJobs();
+  }, [fetchJobs]);
+
+  return {
+    jobs,
+    loading,
+    error,
+    hasMore,
+    refresh,
+    loadMore,
+  };
+};
+
+// 工具函数:格式化薪资显示
+export const formatSalary = (min: number, max: number): string => {
+  if (min === max) return `¥${min.toLocaleString()}/月`;
+  return `¥${min.toLocaleString()}-${max.toLocaleString()}/月`;
+};
+
+// 工具函数:格式化发布时间
+export const formatPublishDate = (date: string): string => {
+  const publishDate = new Date(date);
+  const now = new Date();
+  const diffDays = Math.floor((now.getTime() - publishDate.getTime()) / (1000 * 60 * 60 * 24));
+  
+  if (diffDays === 0) return '今天';
+  if (diffDays === 1) return '昨天';
+  if (diffDays < 7) return `${diffDays}天前`;
+  if (diffDays < 30) return `${Math.floor(diffDays / 7)}周前`;
+  return `${Math.floor(diffDays / 30)}月前`;
+};
+
+// 工具函数:根据工作类型获取对应图标
+export const getJobTypeIcon = (jobType: string): string => {
+  switch (jobType) {
+    case '全职':
+      return '💼';
+    case '兼职':
+      return '⏰';
+    case '实习':
+      return '📚';
+    default:
+      return '🏢';
+  }
+};

+ 92 - 6
src/client/mobile/pages/SilverJobsPage.tsx

@@ -1,13 +1,99 @@
-import React from 'react';
+import React, { useState } from 'react';
+import { JobCard } from '@/client/mobile/components/silver-jobs/JobCard';
+import { useJobs, JobItem } from '@/client/mobile/hooks/useJobs';
+import { INK_COLORS } from '@/client/mobile/styles/colors';
 
 const SilverJobsPage: React.FC = () => {
+  const { jobs, loading, error } = useJobs();
+  const [activeFilter, setActiveFilter] = useState('全部');
+
+  const handleJobPress = (job: JobItem) => {
+    console.log('查看岗位详情:', job.title);
+    // TODO: 导航到岗位详情页
+  };
+
+  const filteredJobs = jobs.filter(job => {
+    if (activeFilter === '全部') return true;
+    return job.jobType === activeFilter;
+  });
+
   return (
-    <div className="min-h-screen bg-gray-50">
-      <header className="bg-blue-600 text-white p-4">
-        <h1 className="text-xl font-bold">银龄岗位</h1>
+    <div className="min-h-screen pb-16" style={{ backgroundColor: INK_COLORS.ink.light }}>
+      {/* 头部导航栏 */}
+      <header className="sticky top-0 z-10 shadow-sm border-b px-4 py-4" style={{ backgroundColor: 'rgba(255,255,255,0.8)', borderColor: INK_COLORS.ink.medium }}>
+        <div className="flex items-center justify-between">
+          <div>
+            <h1 className="font-serif text-xl font-bold" style={{ color: INK_COLORS.ink.deep }}>银龄岗位</h1>
+            <p className="text-xs mt-1" style={{ color: INK_COLORS.text.secondary }}>发现适合银龄人才的优质岗位</p>
+          </div>
+          <div className="flex items-center space-x-2">
+            <button className="p-2 rounded-full" style={{ backgroundColor: INK_COLORS.accent.blue + '20', color: INK_COLORS.accent.blue }}>🔍</button>
+            <button className="p-2 rounded-full" style={{ backgroundColor: INK_COLORS.accent.green + '20', color: INK_COLORS.accent.green }}>⚙️</button>
+          </div>
+        </div>
       </header>
-      <div className="p-4">
-        <p className="text-center text-gray-500 py-8">银龄岗位页面开发中...</p>
+
+      {/* 统计信息 */}
+      <div className="mx-4 mb-4 grid grid-cols-3 gap-2 pt-4">
+        <div className="text-center p-3 rounded-xl shadow-sm" style={{ backgroundColor: 'rgba(255,255,255,0.55)' }}>
+          <div className="text-lg font-bold" style={{ color: INK_COLORS.accent.blue }}>{jobs.length}</div>
+          <div className="text-xs mt-1" style={{ color: INK_COLORS.text.secondary }}>岗位总数</div>
+        </div>
+        <div className="text-center p-3 rounded-xl shadow-sm" style={{ backgroundColor: 'rgba(255,255,255,0.55)' }}>
+          <div className="text-lg font-bold" style={{ color: INK_COLORS.accent.green }}>85%</div>
+          <div className="text-xs mt-1" style={{ color: INK_COLORS.text.secondary }}>匹配度</div>
+        </div>
+        <div className="text-center p-3 rounded-xl shadow-sm" style={{ backgroundColor: 'rgba(255,255,255,0.55)' }}>
+          <div className="text-lg font-bold" style={{ color: INK_COLORS.accent.red }}>156</div>
+          <div className="text-xs mt-1" style={{ color: INK_COLORS.text.secondary }}>活跃用户</div>
+        </div>
+      </div>
+
+      {/* 快速筛选 */}
+      <div className="px-4 mb-4">
+        <div className="flex space-x-2 overflow-x-auto pb-2">
+          {['全部', '全职', '兼职', '社区服务', '教育培训'].map((filter) => (
+            <button
+              key={filter}
+              onClick={() => setActiveFilter(filter)}
+              className="px-3 py-2 rounded-full text-xs font-medium whitespace-nowrap transition-colors"
+              style={{
+                backgroundColor: activeFilter === filter ? INK_COLORS.accent.blue : INK_COLORS.border.light,
+                color: activeFilter === filter ? 'white' : INK_COLORS.text.secondary
+              }}
+            >
+              {filter}
+            </button>
+          ))}
+        </div>
+      </div>
+
+      {/* 岗位列表 */}
+      <div className="pb-4">
+        {loading ? (
+          <div className="flex justify-center py-8">
+            <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
+          </div>
+        ) : error ? (
+          <div className="text-center py-8">
+            <p style={{ color: INK_COLORS.accent.red }}>加载失败,请稍后重试</p>
+          </div>
+        ) : filteredJobs.length === 0 ? (
+          <div className="text-center py-8">
+            <p className="text-2xl mb-2">📋</p>
+            <p style={{ color: INK_COLORS.text.secondary }}>暂无符合条件的岗位</p>
+            <button className="mt-4 px-4 py-2 rounded-full text-sm" style={{ backgroundColor: INK_COLORS.accent.blue, color: 'white' }} onClick={() => setActiveFilter('全部')}>查看全部岗位</button>
+          </div>
+        ) : (
+          filteredJobs.map((job) => (
+            <JobCard key={job.id} job={job} onPress={() => handleJobPress(job)} />
+          ))
+        )}
+      </div>
+
+      {/* 底部提示 */}
+      <div className="text-center py-4">
+        <p className="text-xs" style={{ color: INK_COLORS.text.light }}>下拉可刷新岗位列表</p>
       </div>
     </div>
   );

+ 78 - 0
src/client/mobile/styles/colors.ts

@@ -0,0 +1,78 @@
+/**
+ * 银龄智慧移动端水墨风色彩系统
+ * 基于中国水墨风格设计的色彩定义
+ */
+
+export const INK_COLORS = {
+  // 基础水墨色彩
+  ink: {
+    light: '#f5f3f0',     // 宣纸背景色 - 最浅的底色
+    medium: '#d4c4a8',    // 淡墨 - 次要元素边框
+    dark: '#8b7355',      // 浓墨 - 主要文字和强调
+    deep: '#3a2f26',      // 焦墨 - 标题和重点文字
+  },
+  
+  // 点缀色彩 - 传统国画颜料
+  accent: {
+    red: '#a85c5c',       // 朱砂 - 操作按钮、警示
+    blue: '#4a6b7c',      // 花青 - 信息提示、链接
+    green: '#5c7c5c',     // 石绿 - 成功状态、正向反馈
+  },
+
+  // 语义色彩
+  semantic: {
+    success: '#5c7c5c',   // 成功 - 石绿色
+    warning: '#a85c5c',   // 警告 - 朱砂色
+    error: '#a85c5c',     // 错误 - 朱砂色
+    info: '#4a6b7c',      // 信息 - 花青色
+  },
+
+  // 文字色彩
+  text: {
+    primary: '#2f1f0f',   // 主要文字 - 深墨色
+    secondary: '#5d4e3b', // 次要文字 - 淡墨色
+    light: '#8b7355',     // 辅助文字 - 极淡墨色
+    placeholder: '#b8a082', // 占位文字 - 更淡墨色
+  },
+
+  // 背景色彩
+  background: {
+    page: '#f5f3f0',      // 页面背景 - 宣纸色
+    card: 'rgba(255,255,255,0.8)', // 卡片背景 - 半透明白色
+    cardHover: 'rgba(255,255,255,0.9)', // 卡片悬停 - 更不透明
+    transparent: 'rgba(255,255,255,0.6)', // 透明背景
+  },
+
+  // 边框色彩
+  border: {
+    light: 'rgba(212,196,168,0.3)', // 浅色边框
+    medium: 'rgba(212,196,168,0.5)', // 中等边框
+    dark: 'rgba(212,196,168,0.8)',  // 深色边框
+  },
+
+  // 阴影色彩
+  shadow: {
+    light: 'rgba(58,47,38,0.08)',  // 浅色阴影
+    medium: 'rgba(58,47,38,0.12)', // 中等阴影
+    dark: 'rgba(58,47,38,0.16)',   // 深色阴影
+  }
+} as const;
+
+// CSS变量形式,便于在样式文件中使用
+export const CSS_VARIABLES = {
+  '--ink-light': INK_COLORS.ink.light,
+  '--ink-medium': INK_COLORS.ink.medium,
+  '--ink-dark': INK_COLORS.ink.dark,
+  '--ink-deep': INK_COLORS.ink.deep,
+  '--accent-red': INK_COLORS.accent.red,
+  '--accent-blue': INK_COLORS.accent.blue,
+  '--accent-green': INK_COLORS.accent.green,
+  '--text-primary': INK_COLORS.text.primary,
+  '--text-secondary': INK_COLORS.text.secondary,
+  '--text-light': INK_COLORS.text.light,
+  '--text-placeholder': INK_COLORS.text.placeholder,
+  '--bg-page': INK_COLORS.background.page,
+  '--bg-card': INK_COLORS.background.card,
+  '--border-light': INK_COLORS.border.light,
+  '--border-medium': INK_COLORS.border.medium,
+} as const;

+ 108 - 0
src/client/mobile/styles/typography.ts

@@ -0,0 +1,108 @@
+/**
+ * 银龄智慧移动端水墨风字体系统
+ * 专为银龄用户优化的字体层级和样式
+ */
+
+export const FONT_STYLES = {
+  // 页面标题 - 最大最重要的标题
+  pageTitle: {
+    className: 'font-serif text-3xl font-bold tracking-wide leading-tight',
+    style: { color: 'var(--ink-deep)' }
+  },
+  
+  // 区块标题 - 页面内主要区块标题
+  sectionTitle: {
+    className: 'font-serif text-2xl font-semibold tracking-wide leading-tight',
+    style: { color: 'var(--ink-deep)' }
+  },
+  
+  // 卡片标题 - 列表项或卡片内的标题
+  cardTitle: {
+    className: 'font-serif text-xl font-medium tracking-normal leading-snug',
+    style: { color: 'var(--text-primary)' }
+  },
+  
+  // 正文文字 - 主要内容文字
+  body: {
+    className: 'font-sans text-base leading-relaxed antialiased',
+    style: { color: 'var(--text-primary)' }
+  },
+  
+  // 次要文字 - 辅助说明文字
+  bodySecondary: {
+    className: 'font-sans text-base leading-relaxed antialiased',
+    style: { color: 'var(--text-secondary)' }
+  },
+  
+  // 说明文字 - 标签、提示等
+  caption: {
+    className: 'font-sans text-sm leading-normal',
+    style: { color: 'var(--text-secondary)' }
+  },
+  
+  // 小字文字 - 时间戳、辅助信息
+  small: {
+    className: 'font-sans text-xs leading-snug',
+    style: { color: 'var(--text-light)' }
+  },
+  
+  // 按钮文字
+  button: {
+    className: 'font-sans text-base font-medium leading-none',
+    style: { color: 'inherit' }
+  },
+  
+  // 标签文字
+  label: {
+    className: 'font-sans text-sm font-medium leading-none tracking-wide',
+    style: { color: 'var(--text-secondary)' }
+  }
+} as const;
+
+// 字体大小映射(适老化优化)
+export const FONT_SIZES = {
+  xs: '0.75rem',    // 12px - 标签、辅助信息
+  sm: '0.875rem',   // 14px - 说明文字
+  base: '1rem',     // 16px - 正文文字(标准,适老化)
+  lg: '1.125rem',   // 18px - 小标题(适老化加大)
+  xl: '1.25rem',    // 20px - 卡片标题
+  '2xl': '1.5rem',  // 24px - 区块标题
+  '3xl': '2rem',    // 32px - 页面标题
+} as const;
+
+// 行高映射(适老化优化)
+export const LINE_HEIGHTS = {
+  none: 1,          // 按钮、标签
+  tight: 1.2,       // 标题
+  snug: 1.375,      // 卡片标题
+  normal: 1.6,      // 正文(适老化加大)
+  relaxed: 1.75,    // 长文本(适老化加大)
+} as const;
+
+// 字重映射
+export const FONT_WEIGHTS = {
+  normal: 400,
+  medium: 500,
+  semibold: 600,
+  bold: 700,
+} as const;
+
+// 响应式字体大小映射
+export const RESPONSIVE_FONTS = {
+  mobile: {
+    pageTitle: 'text-2xl',    // 32px -> 28px (移动端略小)
+    sectionTitle: 'text-xl', // 24px -> 20px
+    cardTitle: 'text-lg',     // 20px -> 18px
+    body: 'text-base',        // 16px 保持不变
+    caption: 'text-sm',       // 14px 保持不变
+    small: 'text-sm',         // 12px -> 14px (适老化加大)
+  },
+  tablet: {
+    pageTitle: 'text-3xl',    // 32px
+    sectionTitle: 'text-2xl', // 24px
+    cardTitle: 'text-xl',     // 20px
+    body: 'text-base',        // 16px
+    caption: 'text-sm',       // 14px
+    small: 'text-xs',         // 12px
+  }
+} as const;