lv 1 miesiąc temu
rodzic
commit
9ebf0b1ec5

+ 15 - 1
app.json

@@ -7,6 +7,9 @@
     "icon": "./assets/images/icon.png",
     "scheme": "loanassistant",
     "userInterfaceStyle": "light",
+    "runtimeVersion": {
+      "policy": "appVersion" 
+    },
     "ios": {
       "icon": "./assets/expo.icon",
       "bundleIdentifier": "com.cdloan.assistant",
@@ -54,6 +57,17 @@
     "experiments": {
       "typedRoutes": true,
       "reactCompiler": true
-    }
+    },
+    "updates": {
+      "enabled": true,
+      "checkAutomatically": "ON_LOAD",
+      "url": "https://your-domain.com/api/manifest", // 你的服务器地址
+      "fallbackToCacheTimeout": 30000,
+      "codeSigningCertificate": "./code-signing/certificate.pem",
+      "codeSigningMetadata": {
+        "keyid": "main",
+        "alg": "rsa-v1_5-sha256"
+      }
+    },
   }
 }

+ 0 - 7
design/cupertino_azure/DESIGN.md

@@ -80,10 +80,3 @@ Traditional shadows are heavy; we use **Ambient Light** principles.
 *   **Don't** use standard "drop shadows." If it looks like a 2010 web app, the blur radius is too small and the opacity is too high.
 
 
-
-
-1,根据@xx 下的设计生成相关react-native页面
-2,严格遵循 expo,expo-router 模式
-3,更具@xx 下的设计和@De 生成tailwind.config.js 里的通用颜色,尺寸等.颜色尽量使用 tailwind 里已命名颜色
-4,然后使用 nativewind/tailwndcss 来构建最终react-native页面
-5,参考@xx 下的  react-native 页面模版

+ 1081 - 0
design/index.html

@@ -0,0 +1,1081 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <title>助贷征信分析与产品匹配 - UI原型</title>
+  <style>
+    :root {
+      --bg: #f8fafc;
+      --card: #ffffff;
+      --text: #0f172a;
+      --sub: #64748b;
+      --line: #e2e8f0;
+      --primary: #2563eb;
+      --ok: #16a34a;
+      --warn: #f59e0b;
+      --danger: #dc2626;
+      --radius: 12px;
+    }
+    * { box-sizing: border-box; }
+    body {
+      margin: 0;
+      font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
+      background: #eef2ff;
+      color: var(--text);
+      display: grid;
+      place-items: center;
+      min-height: 100vh;
+      padding: 20px;
+    }
+    .phone {
+      width: 390px;
+      height: 844px;
+      background: var(--bg);
+      border-radius: 32px;
+      border: 8px solid #0b1220;
+      position: relative;
+      overflow: hidden;
+      box-shadow: 0 20px 60px rgba(15, 23, 42, 0.35);
+    }
+    .status {
+      height: 34px;
+      font-size: 12px;
+      color: #334155;
+      padding: 10px 16px 0;
+      display: flex;
+      justify-content: space-between;
+      background: #f1f5f9;
+    }
+    .screen {
+      display: none;
+      height: calc(100% - 34px - 68px);
+      overflow-y: auto;
+      padding: 16px;
+    }
+    .screen.active { display: block; }
+    .screen.no-tab {
+      height: calc(100% - 34px);
+      padding-bottom: 28px;
+    }
+    h1 {
+      font-size: 20px;
+      margin: 0 0 8px;
+    }
+    h2 {
+      font-size: 16px;
+      margin: 0 0 8px;
+    }
+    .sub {
+      color: var(--sub);
+      font-size: 13px;
+      margin-bottom: 14px;
+    }
+    .card {
+      background: var(--card);
+      border: 1px solid var(--line);
+      border-radius: var(--radius);
+      padding: 12px;
+      margin-bottom: 12px;
+    }
+    .row {
+      display: flex;
+      gap: 8px;
+    }
+    .between {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      gap: 8px;
+    }
+    .stat {
+      flex: 1;
+      background: #eff6ff;
+      border: 1px solid #bfdbfe;
+      border-radius: 10px;
+      padding: 10px;
+      text-align: center;
+    }
+    .stat b { font-size: 18px; display: block; }
+    .input {
+      width: 100%;
+      border: 1px solid var(--line);
+      border-radius: 10px;
+      padding: 11px 12px;
+      margin-bottom: 10px;
+      background: #fff;
+      font-size: 14px;
+    }
+    .btn {
+      width: 100%;
+      border: none;
+      border-radius: 10px;
+      padding: 12px 14px;
+      font-size: 14px;
+      font-weight: 700;
+      cursor: pointer;
+      margin-top: 4px;
+    }
+    .btn.primary { background: var(--primary); color: #fff; }
+    .btn.ghost { background: #fff; color: var(--primary); border: 1px solid #bfdbfe; }
+    .btn.ok { background: var(--ok); color: #fff; }
+    .tag {
+      display: inline-block;
+      font-size: 12px;
+      border-radius: 999px;
+      padding: 3px 8px;
+      margin: 0 6px 6px 0;
+      background: #eff6ff;
+      color: var(--primary);
+    }
+    .tag.warn { background: #fef3c7; color: #92400e; }
+    .tag.danger { background: #fee2e2; color: #991b1b; }
+    .small { font-size: 12px; color: var(--sub); }
+    .progress {
+      height: 10px;
+      background: #e2e8f0;
+      border-radius: 999px;
+      overflow: hidden;
+      margin: 10px 0;
+    }
+    .bar {
+      height: 100%;
+      background: linear-gradient(90deg, #2563eb, #06b6d4);
+      width: 72%;
+    }
+    .score {
+      width: 110px;
+      height: 110px;
+      border-radius: 50%;
+      margin: 2px auto 10px;
+      border: 10px solid #bfdbfe;
+      display: grid;
+      place-items: center;
+      color: var(--primary);
+      font-weight: 800;
+      font-size: 26px;
+      background: #f8fbff;
+    }
+    .product {
+      border: 1px solid #bfdbfe;
+      background: #f8fbff;
+    }
+    .product h3 {
+      margin: 0 0 8px;
+      font-size: 15px;
+    }
+    .bottom-tab {
+      position: absolute;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      height: 68px;
+      background: #fff;
+      border-top: 1px solid var(--line);
+      display: flex;
+      justify-content: space-around;
+      padding-top: 8px;
+    }
+    .tab {
+      border: none;
+      background: transparent;
+      color: #64748b;
+      font-size: 12px;
+      cursor: pointer;
+    }
+    .tab.active { color: var(--primary); font-weight: 700; }
+    .link {
+      font-size: 13px;
+      color: var(--primary);
+      text-decoration: none;
+      cursor: pointer;
+    }
+    .space { height: 8px; }
+    .ai-fab {
+      position: absolute;
+      right: 14px;
+      bottom: 86px;
+      z-index: 8;
+      border: none;
+      border-radius: 999px;
+      background: linear-gradient(135deg, #2563eb, #0ea5e9);
+      color: #fff;
+      font-weight: 700;
+      font-size: 12px;
+      padding: 10px 12px;
+      box-shadow: 0 10px 18px rgba(37, 99, 235, 0.3);
+      cursor: pointer;
+    }
+    .ai-fab.hide { display: none; }
+    .ai-panel {
+      position: absolute;
+      left: 10px;
+      right: 10px;
+      bottom: 10px;
+      z-index: 9;
+      background: #fff;
+      border: 1px solid var(--line);
+      border-radius: 16px;
+      box-shadow: 0 20px 30px rgba(15, 23, 42, 0.2);
+      display: none;
+      max-height: 62%;
+      overflow: hidden;
+    }
+    .ai-panel.show { display: block; }
+    .ai-head {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 10px 12px;
+      border-bottom: 1px solid var(--line);
+      background: #f8fbff;
+    }
+    .ai-title {
+      font-size: 14px;
+      font-weight: 700;
+      color: #1e3a8a;
+    }
+    .ai-close {
+      border: none;
+      background: transparent;
+      color: #64748b;
+      font-size: 18px;
+      cursor: pointer;
+    }
+    .ai-body {
+      padding: 10px 12px 12px;
+      overflow-y: auto;
+      max-height: calc(62vh - 48px);
+    }
+    .ai-msg {
+      background: #eff6ff;
+      border: 1px solid #bfdbfe;
+      border-radius: 10px;
+      padding: 10px;
+      font-size: 13px;
+      margin-bottom: 10px;
+      color: #1e3a8a;
+    }
+    .ai-q {
+      border: 1px solid #dbeafe;
+      background: #fff;
+      color: #1d4ed8;
+      border-radius: 999px;
+      padding: 7px 10px;
+      margin: 0 6px 8px 0;
+      font-size: 12px;
+      cursor: pointer;
+    }
+    .ai-input {
+      flex: 1;
+      border: 1px solid var(--line);
+      border-radius: 10px;
+      padding: 10px;
+      font-size: 13px;
+      margin-top: 0;
+    }
+    .ai-tip {
+      margin-top: 8px;
+      font-size: 12px;
+      color: var(--sub);
+    }
+    .ai-chat {
+      max-height: 170px;
+      overflow-y: auto;
+      margin: 4px 0 10px;
+    }
+    .ai-bubble {
+      border-radius: 10px;
+      padding: 8px 10px;
+      font-size: 13px;
+      margin-bottom: 8px;
+      max-width: 92%;
+      line-height: 1.45;
+      white-space: pre-wrap;
+    }
+    .ai-bubble.user {
+      margin-left: auto;
+      background: #2563eb;
+      color: #fff;
+    }
+    .ai-bubble.ai {
+      background: #f8fafc;
+      border: 1px solid var(--line);
+      color: #0f172a;
+    }
+    .ai-input-row {
+      display: flex;
+      gap: 8px;
+      align-items: center;
+    }
+    .ai-send {
+      border: none;
+      border-radius: 10px;
+      background: #2563eb;
+      color: #fff;
+      font-size: 12px;
+      font-weight: 700;
+      padding: 10px 12px;
+      cursor: pointer;
+      white-space: nowrap;
+    }
+    .ai-loading {
+      color: #64748b;
+      font-size: 12px;
+    }
+    .ai-result h4 {
+      margin: 0 0 6px;
+      font-size: 13px;
+      color: #1e3a8a;
+    }
+    .ai-result p {
+      margin: 0 0 6px;
+      font-size: 12px;
+      color: #334155;
+    }
+    .ai-open-report {
+      margin-top: 6px;
+      width: auto;
+      padding: 8px 10px;
+      font-size: 12px;
+      border-radius: 8px;
+    }
+    .report-page .card {
+      margin-bottom: 10px;
+    }
+    .report-badge {
+      display: inline-block;
+      font-size: 12px;
+      color: #1d4ed8;
+      background: #eff6ff;
+      border: 1px solid #bfdbfe;
+      border-radius: 999px;
+      padding: 4px 9px;
+      margin-bottom: 8px;
+    }
+    .report-block-title {
+      font-size: 13px;
+      font-weight: 700;
+      color: #1e3a8a;
+      margin: 0 0 6px;
+    }
+    .report-content {
+      font-size: 13px;
+      line-height: 1.6;
+      color: #334155;
+      margin: 0;
+      white-space: pre-wrap;
+    }
+    .quick-row {
+      display: flex;
+      gap: 8px;
+    }
+    .quick-btn {
+      margin-top: 8px;
+      padding: 9px 10px;
+      font-size: 12px;
+    }
+    .pref-row {
+      display: flex;
+      gap: 8px;
+      margin: 6px 0 8px;
+    }
+    .pref-btn {
+      border: 1px solid #bfdbfe;
+      background: #fff;
+      color: #1d4ed8;
+      border-radius: 999px;
+      padding: 6px 10px;
+      font-size: 12px;
+      cursor: pointer;
+    }
+    .pref-btn.active {
+      background: #2563eb;
+      color: #fff;
+      border-color: #2563eb;
+    }
+  </style>
+</head>
+<body>
+  <div class="phone">
+    <div class="status">
+      <span>9:41</span>
+      <span>5G 100%</span>
+    </div>
+
+    <section id="login" class="screen no-tab active">
+      <h1>助贷智能工作台</h1>
+      <p class="sub">征信分析、自动评估、一键匹配银行产品</p>
+      <div class="card">
+        <h2>登录</h2>
+        <input class="input" placeholder="手机号" />
+        <input class="input" placeholder="验证码" />
+        <button class="btn primary" data-go="home">进入系统</button>
+        <button class="btn ghost" data-go="register">去注册</button>
+        <p class="small">登录即表示同意《用户协议》《隐私政策》</p>
+      </div>
+    </section>
+
+    <section id="register" class="screen no-tab">
+      <h1>注册账号</h1>
+      <p class="sub">面向助贷人员注册,完成后可管理多个客户</p>
+      <div class="card">
+        <input class="input" placeholder="姓名" />
+        <input class="input" placeholder="手机号" />
+        <input class="input" placeholder="验证码" />
+        <input class="input" placeholder="机构名称" />
+        <input class="input" placeholder="执业城市" />
+        <button class="btn primary" data-go="home">注册并进入系统</button>
+      </div>
+      <a class="link" data-go="login">返回登录</a>
+    </section>
+
+    <section id="home" class="screen">
+      <h1>首页</h1>
+      <p class="sub">你好,王经理。今天有 6 位客户待跟进。</p>
+      <div class="row">
+        <div class="stat"><b>6</b><span class="small">待处理</span></div>
+        <div class="stat"><b>2</b><span class="small">分析中</span></div>
+        <div class="stat"><b>3</b><span class="small">可匹配</span></div>
+      </div>
+      <div class="space"></div>
+      <div class="card">
+        <h2>快捷操作</h2>
+        <div class="row">
+          <button class="btn primary" data-go="add-customer">新增客户</button>
+          <button class="btn ghost" data-go="upload">征信分析</button>
+        </div>
+      </div>
+      <div class="card">
+        <div class="between">
+          <h2>最近客户</h2>
+          <a class="link" data-go="customers">查看全部</a>
+        </div>
+        <p>张三 · 征信已分析 · 等待匹配</p>
+        <p>李四 · 上传中</p>
+      </div>
+    </section>
+
+    <section id="customers" class="screen">
+      <h1>客户</h1>
+      <input class="input" placeholder="搜索客户姓名 / 手机号" />
+      <div>
+        <span class="tag">全部</span>
+        <span class="tag warn">待上传</span>
+        <span class="tag">已分析</span>
+        <span class="tag">已匹配</span>
+      </div>
+      <div class="card">
+        <div class="between"><b>张三</b><span class="small">138****8888</span></div>
+        <p class="small">状态:征信分析完成(B+)</p>
+        <button class="btn primary" data-go="report">查看报告</button>
+        <button class="btn ghost" data-go="edit-customer">编辑资料</button>
+      </div>
+      <div class="card">
+        <div class="between"><b>李四</b><span class="small">139****6666</span></div>
+        <p class="small">状态:已上传,解析中</p>
+        <button class="btn ghost" data-go="processing">查看进度</button>
+        <button class="btn ghost" data-go="edit-customer">编辑资料</button>
+      </div>
+      <button class="btn primary" data-go="add-customer">+ 新增客户</button>
+    </section>
+
+    <section id="edit-customer" class="screen no-tab">
+      <h1>编辑客户资料</h1>
+      <p class="sub">可随时修改客户信息,保存后即时生效(UI演示)</p>
+      <div class="card">
+        <input class="input" value="张三" />
+        <input class="input" value="13800008888" />
+        <input class="input" value="身份证后四位:1234" />
+        <input class="input" value="职业:个体经营" />
+        <input class="input" value="备注:近期有贷款需求,关注放款速度" />
+        <button id="saveCustomerBtn" class="btn primary">保存客户资料</button>
+        <p id="customerSaveTip" class="small" style="display:none;">客户资料已保存(UI演示)。</p>
+      </div>
+      <a class="link" data-go="customers">返回客户列表</a>
+    </section>
+
+    <section id="add-customer" class="screen no-tab">
+      <h1>新增客户</h1>
+      <p class="sub">先建客户档案,再进入征信分析</p>
+      <div class="card">
+        <input class="input" placeholder="客户姓名" />
+        <input class="input" placeholder="手机号" />
+        <input class="input" placeholder="身份证后四位(可选)" />
+        <input class="input" placeholder="备注(可选)" />
+        <button class="btn primary" data-go="upload">保存并进入征信分析</button>
+      </div>
+      <a class="link" data-go="home">返回首页</a>
+    </section>
+
+    <section id="upload" class="screen">
+      <h1>征信分析</h1>
+      <div class="card">
+        <h2>选择客户</h2>
+        <input class="input" value="张三(138****8888)" />
+      </div>
+      <div class="card">
+        <h2>选择文件</h2>
+        <p class="small">上传入口不限制文件类型,具体校验由程序处理。</p>
+        <button class="btn ghost">选择文件</button>
+      </div>
+      <button class="btn primary" data-go="processing">开始解析</button>
+    </section>
+
+    <section id="processing" class="screen no-tab">
+      <h1>征信解析中</h1>
+      <p class="sub">正在解析征信数据,预计30-90秒</p>
+      <div class="card">
+        <div class="progress"><div class="bar"></div></div>
+        <p class="small">当前进度:72%</p>
+        <p class="small">你可以稍后在“报告”页查看结果</p>
+      </div>
+      <button class="btn ok" data-go="report">模拟解析完成</button>
+      <a class="link" data-go="home">返回首页</a>
+    </section>
+
+    <section id="report" class="screen">
+      <h1>征信分析报告</h1>
+      <p class="sub">客户:张三(最近更新:今天 10:42)</p>
+      <div class="card">
+        <div class="score">B+</div>
+        <p style="text-align:center;margin:0 0 8px;">建议可申请额度:20万-35万</p>
+        <div style="text-align:center;">
+          <span class="tag">收入稳定</span>
+          <span class="tag warn">查询偏多</span>
+          <span class="tag danger">负债率偏高</span>
+        </div>
+      </div>
+      <div class="card">
+        <h2>关键指标</h2>
+        <p class="small">近3个月查询次数:8 次</p>
+        <p class="small">信用卡使用率:72%</p>
+        <p class="small">当前逾期:无</p>
+      </div>
+      <div class="card">
+        <h2>建议动作</h2>
+        <p class="small">1. 控制近1个月新增查询</p>
+        <p class="small">2. 优先匹配看重流水稳定的产品</p>
+      </div>
+      <button class="btn primary" data-go="match">智能匹配银行产品</button>
+    </section>
+
+    <section id="match" class="screen">
+      <h1>智能匹配结果(Top 3)</h1>
+      <p class="sub">根据当前征信表现,推荐以下更合适产品</p>
+      <div class="card product">
+        <h3>产品A · 稳享贷 <span class="tag">匹配度 92%</span></h3>
+        <p class="small">额度:20-30万 | 年化:4.2%-6.1% | 放款:T+1</p>
+        <p class="small">匹配理由:收入稳定、无当前逾期、近半年账户活跃</p>
+        <button class="btn ghost" data-go="product">查看详情</button>
+      </div>
+      <div class="card product">
+        <h3>产品B · 惠民贷 <span class="tag">匹配度 88%</span></h3>
+        <p class="small">额度:15-25万 | 年化:4.8%-6.8% | 放款:T+1~T+2</p>
+        <p class="small">匹配理由:查询可接受、负债可覆盖、工作稳定</p>
+        <button class="btn ghost" data-go="product">查看详情</button>
+      </div>
+      <div class="card product">
+        <h3>产品C · 极速贷 <span class="tag">匹配度 84%</span></h3>
+        <p class="small">额度:10-20万 | 年化:5.2%-7.2% | 放款:最快当天</p>
+        <p class="small">匹配理由:审批快、对查询容忍较高、材料简化</p>
+        <button class="btn ghost" data-go="product">查看详情</button>
+      </div>
+      <button id="rematchBtn" class="btn primary">重新匹配(调整偏好)</button>
+      <div id="rematchPanel" class="card" style="display:none;">
+        <h2>匹配偏好</h2>
+        <div class="pref-row">
+          <button class="pref-btn active" data-pref="额度优先">额度优先</button>
+          <button class="pref-btn" data-pref="利率优先">利率优先</button>
+          <button class="pref-btn" data-pref="放款速度优先">放款速度优先</button>
+        </div>
+        <button id="confirmRematchBtn" class="btn primary">确认重新匹配</button>
+        <p id="rematchResult" class="small" style="display:none;">已按“额度优先”重新匹配,结果已更新(UI演示)。</p>
+      </div>
+    </section>
+
+    <section id="product" class="screen no-tab">
+      <h1>产品A · 稳享贷</h1>
+      <p class="sub">银行直连产品,适合征信中等偏优客户</p>
+      <div class="card">
+        <h2>产品参数</h2>
+        <p class="small">额度区间:20万-30万</p>
+        <p class="small">年化区间:4.2%-6.1%</p>
+        <p class="small">期限:12-36期</p>
+        <p class="small">放款时效:T+1</p>
+      </div>
+      <div class="card">
+        <h2>准入条件</h2>
+        <p class="small">- 年龄22-55周岁</p>
+        <p class="small">- 近6个月无当前逾期</p>
+        <p class="small">- 查询次数建议低于10次/3个月</p>
+      </div>
+      <div class="card">
+        <h2>风险提示</h2>
+        <p class="small">若近期新增2次以上硬查询,审批通过率可能明显下降。</p>
+      </div>
+      <button class="btn primary">标记推荐给客户</button>
+      <a class="link" data-go="match">返回匹配结果</a>
+    </section>
+
+    <section id="reports" class="screen">
+      <h1>报告</h1>
+      <div class="card">
+        <div class="between"><b>张三</b><span class="tag">B+</span></div>
+        <p class="small">已生成征信分析报告 · 10:42</p>
+        <button class="btn ghost" data-go="report">查看</button>
+      </div>
+      <div class="card">
+        <div class="between"><b>王五</b><span class="tag warn">解析中</span></div>
+        <p class="small">上传时间:09:16</p>
+      </div>
+    </section>
+
+    <section id="ai-report" class="screen no-tab report-page">
+      <h1>AI专业回答</h1>
+      <span class="report-badge" id="aiReportScene">场景:报告解读</span>
+      <div class="card">
+        <h2 id="aiReportQuestionTitle">用户问题</h2>
+        <p id="aiReportQuestion" class="report-content">-</p>
+      </div>
+      <div class="card">
+        <p class="report-block-title">结论</p>
+        <p id="aiReportConclusion" class="report-content">-</p>
+      </div>
+      <div class="card">
+        <p class="report-block-title">依据</p>
+        <p id="aiReportBasis" class="report-content">-</p>
+      </div>
+      <div class="card">
+        <p class="report-block-title">建议动作</p>
+        <p id="aiReportAction" class="report-content">-</p>
+      </div>
+      <div class="card">
+        <p class="report-block-title">风险提示</p>
+        <p id="aiReportRisk" class="report-content">-</p>
+      </div>
+      <button id="aiReportBack" class="btn primary">回提问界面</button>
+      <div class="quick-row">
+        <button id="aiReportBackStart" class="btn ghost quick-btn">回开始提问页</button>
+        <button id="aiReportHome" class="btn ghost quick-btn">一键回首页</button>
+      </div>
+    </section>
+
+    <section id="mine" class="screen">
+      <h1>我的</h1>
+      <div class="card">
+        <h2>账号信息</h2>
+        <p class="small">姓名:王经理</p>
+        <p class="small">机构:XX助贷服务</p>
+        <p class="small">城市:上海</p>
+      </div>
+      <div class="card">
+        <h2>设置</h2>
+        <p class="small">隐私协议</p>
+        <p class="small">联系客服</p>
+        <p class="small">版本信息 v1.0</p>
+      </div>
+      <button class="btn primary" data-go="edit-mine">编辑我的资料</button>
+      <button class="btn ghost" data-go="login">退出登录</button>
+    </section>
+
+    <section id="edit-mine" class="screen no-tab">
+      <h1>编辑我的资料</h1>
+      <p class="sub">可修改个人与机构信息,便于统一对外展示</p>
+      <div class="card">
+        <input class="input" value="王经理" />
+        <input class="input" value="13800001111" />
+        <input class="input" value="XX助贷服务" />
+        <input class="input" value="上海" />
+        <input class="input" value="擅长:经营贷、消费贷匹配" />
+        <button id="saveMineBtn" class="btn primary">保存我的资料</button>
+        <p id="mineSaveTip" class="small" style="display:none;">我的资料已保存(UI演示)。</p>
+      </div>
+      <a class="link" data-go="mine">返回我的</a>
+    </section>
+
+    <nav class="bottom-tab" id="tabs">
+      <button class="tab active" data-tab="home">首页</button>
+      <button class="tab" data-tab="customers">客户</button>
+      <button class="tab" data-tab="upload">征信分析</button>
+      <button class="tab" data-tab="reports">报告</button>
+      <button class="tab" data-tab="mine">我的</button>
+    </nav>
+
+    <button id="aiFab" class="ai-fab">AI助理</button>
+    <div id="aiPanel" class="ai-panel">
+      <div class="ai-head">
+        <span id="aiTitle" class="ai-title">AI助理</span>
+        <button id="aiClose" class="ai-close" aria-label="关闭">×</button>
+      </div>
+      <div class="ai-body">
+        <div id="aiIntro" class="ai-msg"></div>
+        <div id="aiQuestions"></div>
+        <div id="aiChat" class="ai-chat"></div>
+        <div class="ai-input-row">
+          <input id="aiInput" class="ai-input" placeholder="输入你的问题(UI演示,不发起真实请求)" />
+          <button id="aiSend" class="ai-send">发送</button>
+        </div>
+        <p id="aiTip" class="ai-tip"></p>
+        <div class="quick-row">
+          <button id="aiGoStart" class="btn ghost quick-btn">回开始提问页</button>
+          <button id="aiGoHome" class="btn ghost quick-btn">一键回首页</button>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <script>
+    const screens = document.querySelectorAll(".screen");
+    const tabs = document.querySelectorAll(".tab");
+    const tabsWrap = document.getElementById("tabs");
+    const aiFab = document.getElementById("aiFab");
+    const aiPanel = document.getElementById("aiPanel");
+    const aiClose = document.getElementById("aiClose");
+    const aiTitle = document.getElementById("aiTitle");
+    const aiIntro = document.getElementById("aiIntro");
+    const aiQuestions = document.getElementById("aiQuestions");
+    const aiChat = document.getElementById("aiChat");
+    const aiInput = document.getElementById("aiInput");
+    const aiSend = document.getElementById("aiSend");
+    const aiTip = document.getElementById("aiTip");
+    const aiReportScene = document.getElementById("aiReportScene");
+    const aiReportQuestion = document.getElementById("aiReportQuestion");
+    const aiReportConclusion = document.getElementById("aiReportConclusion");
+    const aiReportBasis = document.getElementById("aiReportBasis");
+    const aiReportAction = document.getElementById("aiReportAction");
+    const aiReportRisk = document.getElementById("aiReportRisk");
+    const aiReportBack = document.getElementById("aiReportBack");
+    const aiReportBackStart = document.getElementById("aiReportBackStart");
+    const aiReportHome = document.getElementById("aiReportHome");
+    const aiGoStart = document.getElementById("aiGoStart");
+    const aiGoHome = document.getElementById("aiGoHome");
+    const rematchBtn = document.getElementById("rematchBtn");
+    const rematchPanel = document.getElementById("rematchPanel");
+    const confirmRematchBtn = document.getElementById("confirmRematchBtn");
+    const rematchResult = document.getElementById("rematchResult");
+    const saveCustomerBtn = document.getElementById("saveCustomerBtn");
+    const customerSaveTip = document.getElementById("customerSaveTip");
+    const saveMineBtn = document.getElementById("saveMineBtn");
+    const mineSaveTip = document.getElementById("mineSaveTip");
+    let currentScreenId = "login";
+    let beforeAiReportScreenId = "home";
+    let aiSessionStartScreenId = "home";
+    let lastAiResponse = null;
+    let lastAiQuestion = "";
+    let selectedPref = "额度优先";
+
+    const aiContext = {
+      home: {
+        title: "AI助理 · 今日工作建议",
+        intro: "我可以基于待处理客户优先级,给你今天最有效的跟进顺序。",
+        questions: [
+          "先跟进哪3位客户成功率最高?",
+          "今天优先做征信分析还是先做产品匹配?",
+          "给我一份30分钟高效处理清单。"
+        ]
+      },
+      customers: {
+        title: "AI助理 · 客户策略",
+        intro: "我可以按客户状态给你建议:先补资料、先分析,还是先沟通预期。",
+        questions: [
+          "这个客户现在最该做什么?",
+          "如何向客户解释‘分析中’不等于‘能通过’?",
+          "给我一段专业但好懂的沟通话术。"
+        ]
+      },
+      upload: {
+        title: "AI助理 · 征信分析前",
+        intro: "我可以告诉你分析前要核对的信息,避免后续匹配偏差。",
+        questions: [
+          "开始分析前我需要确认哪些客户信息?",
+          "如何减少因资料缺失导致的误判?",
+          "给我一段让客户配合补充资料的话术。"
+        ]
+      },
+      processing: {
+        title: "AI助理 · 解析等待中",
+        intro: "等待期间可提前准备沟通策略,分析完成后直接进入匹配。",
+        questions: [
+          "结果出来后第一句话怎么跟客户说?",
+          "如果评级一般,怎么引导客户接受方案?",
+          "先讲风险还是先讲可做产品?"
+        ]
+      },
+      report: {
+        title: "AI助理 · 报告解读",
+        intro: "我可以把征信指标翻译成可执行建议:结论、依据、动作、风险提示。",
+        questions: [
+          "这份征信最核心的3个风险是什么?",
+          "为什么是B+,不是A?",
+          "未来30天怎么优化通过率?"
+        ]
+      },
+      match: {
+        title: "AI助理 · 产品对比",
+        intro: "我可以解释Top3推荐差异,并按‘额度/利率/速度’给排序建议。",
+        questions: [
+          "这3个产品该怎么给客户讲差异?",
+          "客户要低利率,应该优先推荐哪个?",
+          "客户要快放款,顺序怎么排?"
+        ]
+      },
+      product: {
+        title: "AI助理 · 进件建议",
+        intro: "我可以根据该产品准入条件,给你进件前风险提醒和补件建议。",
+        questions: [
+          "这个客户做该产品的主要风险点是什么?",
+          "进件前还建议补哪些材料?",
+          "如果被拒,备选产品顺序怎么给?"
+        ]
+      },
+      reports: {
+        title: "AI助理 · 报告汇总",
+        intro: "我可以帮你快速定位需要优先复盘的客户报告。",
+        questions: [
+          "哪几份报告最值得今天先处理?",
+          "解析中客户要提前准备什么?",
+          "帮我总结常见风险类型。"
+        ]
+      },
+      mine: {
+        title: "AI助理 · 业务复盘",
+        intro: "我可以帮你做今日复盘,沉淀可复用的话术与流程。",
+        questions: [
+          "今天哪些动作最提升成单概率?",
+          "帮我总结一版标准跟进SOP。",
+          "如何提高客户配合上传资料率?"
+        ]
+      }
+    };
+
+    function renderAiContext(id) {
+      const ctx = aiContext[id] || aiContext.home;
+      aiTitle.textContent = ctx.title;
+      aiIntro.textContent = ctx.intro;
+      aiQuestions.innerHTML = "";
+      aiChat.innerHTML = "";
+      ctx.questions.forEach(q => {
+        const btn = document.createElement("button");
+        btn.className = "ai-q";
+        btn.textContent = q;
+        btn.setAttribute("data-ai-q", q);
+        aiQuestions.appendChild(btn);
+      });
+      aiTip.textContent = "提示:当前为UI演示。输入问题后点击“发送”可查看模拟AI返回结构。";
+    }
+
+    function addUserMessage(text) {
+      const msg = document.createElement("div");
+      msg.className = "ai-bubble user";
+      msg.textContent = text;
+      aiChat.appendChild(msg);
+      aiChat.scrollTop = aiChat.scrollHeight;
+    }
+
+    function buildMockAnswer(screenId) {
+      const answerMap = {
+        report: {
+          conclusion: "该客户当前可做稳健型产品,建议先控查询再推进主推产品。",
+          basis: "依据:近3个月查询次数偏多、信用卡使用率72%、当前无逾期。",
+          action: "动作:1) 7-14天内避免新增硬查询 2) 优先申请看重流水稳定产品 3) 先小额试单。",
+          risk: "风险提示:最终以银行实时审批为准,不承诺100%通过。"
+        },
+        match: {
+          conclusion: "若客户优先低利率,先推产品A;若优先速度,优先产品C。",
+          basis: "依据:A匹配度92%且利率区间更优,C放款时效最快。",
+          action: "动作:先确认客户优先级(利率/额度/速度),再给2选1方案。",
+          risk: "风险提示:若近期新增查询,产品A通过率可能下降。"
+        },
+        product: {
+          conclusion: "该产品适配中等偏优客户,当前客户可尝试但需先做风险沟通。",
+          basis: "依据:准入条件与客户现状基本匹配,查询次数接近敏感阈值。",
+          action: "动作:补充收入证明并先做预审沟通,再正式进件。",
+          risk: "风险提示:若被拒,建议切换容忍查询更高的备选产品。"
+        }
+      };
+      return answerMap[screenId] || {
+        conclusion: "当前阶段建议先明确客户目标,再进入下一步操作。",
+        basis: "依据:页面信息可支持基础判断,但仍需补充客户偏好与近期变化。",
+        action: "动作:先确认目标(额度/利率/放款速度),再执行对应流程。",
+        risk: "风险提示:建议始终向客户说明“最终以银行审批为准”。"
+      };
+    }
+
+    function getSceneName(screenId) {
+      const map = {
+        home: "首页工作建议",
+        customers: "客户策略",
+        upload: "征信分析前",
+        processing: "解析等待中",
+        report: "征信报告解读",
+        match: "产品匹配对比",
+        product: "产品进件建议",
+        reports: "报告汇总",
+        mine: "业务复盘"
+      };
+      return map[screenId] || "综合建议";
+    }
+
+    function fillAiReport(screenId, question, data) {
+      aiReportScene.textContent = `场景:${getSceneName(screenId)}`;
+      aiReportQuestion.textContent = question || "未记录问题";
+      aiReportConclusion.textContent = data.conclusion || "-";
+      aiReportBasis.textContent = data.basis || "-";
+      aiReportAction.textContent = data.action || "-";
+      aiReportRisk.textContent = data.risk || "-";
+    }
+
+    function addAiMessage(screenId, question) {
+      const data = buildMockAnswer(screenId);
+      lastAiResponse = data;
+      lastAiQuestion = question;
+      const wrap = document.createElement("div");
+      wrap.className = "ai-bubble ai ai-result";
+      wrap.innerHTML = `
+        <h4>结论</h4><p>${data.conclusion}</p>
+        <h4>依据</h4><p>${data.basis}</p>
+        <h4>建议动作</h4><p>${data.action}</p>
+        <h4>风险提示</h4><p>${data.risk}</p>
+        <button class="btn ghost ai-open-report" data-ai-open-report="1">查看完整回答</button>
+      `;
+      aiChat.appendChild(wrap);
+      aiChat.scrollTop = aiChat.scrollHeight;
+    }
+
+    function submitAiQuestion() {
+      const q = aiInput.value.trim();
+      if (!q) return;
+      addUserMessage(q);
+      aiInput.value = "";
+      const loading = document.createElement("div");
+      loading.className = "ai-bubble ai ai-loading";
+      loading.textContent = "AI正在生成专业建议...";
+      aiChat.appendChild(loading);
+      aiChat.scrollTop = aiChat.scrollHeight;
+      setTimeout(() => {
+        loading.remove();
+        addAiMessage(currentScreenId, q);
+      }, 550);
+    }
+
+    function updateAiEntry(id) {
+      currentScreenId = id;
+      renderAiContext(id);
+      const hideAi = id === "login" || id === "register";
+      aiFab.classList.toggle("hide", hideAi);
+      if (hideAi) aiPanel.classList.remove("show");
+    }
+
+    function show(id) {
+      screens.forEach(s => s.classList.remove("active"));
+      const target = document.getElementById(id);
+      if (!target) return;
+      target.classList.add("active");
+
+      const noTab = target.classList.contains("no-tab") || id === "login";
+      tabsWrap.style.display = noTab ? "none" : "flex";
+
+      tabs.forEach(t => t.classList.remove("active"));
+      const hit = document.querySelector(`.tab[data-tab="${id}"]`);
+      if (hit) hit.classList.add("active");
+      updateAiEntry(id);
+    }
+
+    tabs.forEach(btn => {
+      btn.addEventListener("click", () => show(btn.dataset.tab));
+    });
+
+    document.body.addEventListener("click", e => {
+      const btn = e.target.closest("[data-go]");
+      if (!btn) return;
+      show(btn.dataset.go);
+    });
+
+    aiFab.addEventListener("click", () => {
+      aiSessionStartScreenId = currentScreenId;
+      renderAiContext(currentScreenId);
+      aiPanel.classList.add("show");
+    });
+
+    aiClose.addEventListener("click", () => {
+      aiPanel.classList.remove("show");
+    });
+
+    aiQuestions.addEventListener("click", e => {
+      const btn = e.target.closest("[data-ai-q]");
+      if (!btn) return;
+      aiInput.value = btn.getAttribute("data-ai-q");
+    });
+
+    aiChat.addEventListener("click", e => {
+      const btn = e.target.closest("[data-ai-open-report]");
+      if (!btn || !lastAiResponse) return;
+      beforeAiReportScreenId = currentScreenId;
+      fillAiReport(currentScreenId, lastAiQuestion, lastAiResponse);
+      aiPanel.classList.remove("show");
+      show("ai-report");
+    });
+
+    aiSend.addEventListener("click", submitAiQuestion);
+
+    aiInput.addEventListener("keydown", e => {
+      if (e.key === "Enter") submitAiQuestion();
+    });
+
+    aiReportBack.addEventListener("click", () => {
+      show(beforeAiReportScreenId);
+      aiPanel.classList.add("show");
+    });
+
+    aiReportBackStart.addEventListener("click", () => {
+      show(aiSessionStartScreenId || "home");
+      aiPanel.classList.add("show");
+    });
+
+    aiReportHome.addEventListener("click", () => {
+      aiPanel.classList.remove("show");
+      show("home");
+    });
+
+    aiGoStart.addEventListener("click", () => {
+      aiPanel.classList.remove("show");
+      show(aiSessionStartScreenId || "home");
+    });
+
+    aiGoHome.addEventListener("click", () => {
+      aiPanel.classList.remove("show");
+      show("home");
+    });
+
+    rematchBtn.addEventListener("click", () => {
+      const opened = rematchPanel.style.display === "block";
+      rematchPanel.style.display = opened ? "none" : "block";
+      if (!opened) rematchResult.style.display = "none";
+    });
+
+    rematchPanel.addEventListener("click", e => {
+      const btn = e.target.closest("[data-pref]");
+      if (!btn) return;
+      selectedPref = btn.getAttribute("data-pref");
+      rematchPanel.querySelectorAll(".pref-btn").forEach(el => el.classList.remove("active"));
+      btn.classList.add("active");
+    });
+
+    confirmRematchBtn.addEventListener("click", () => {
+      rematchResult.textContent = `已按“${selectedPref}”重新匹配,结果已更新(UI演示)。`;
+      rematchResult.style.display = "block";
+    });
+
+    saveCustomerBtn.addEventListener("click", () => {
+      customerSaveTip.style.display = "block";
+    });
+
+    saveMineBtn.addEventListener("click", () => {
+      mineSaveTip.style.display = "block";
+    });
+
+    updateAiEntry("login");
+  </script>
+</body>
+</html>

+ 76 - 0
src/app/(tabs)/_layout.tsx

@@ -0,0 +1,76 @@
+import { Colors } from '@/constants/theme';
+import { useAuth } from '@/utils/auth';
+import { router } from 'expo-router';
+import { NativeTabs } from 'expo-router/unstable-native-tabs';
+import React, { useEffect } from 'react';
+import { useColorScheme } from 'react-native';
+
+/**
+ * Tab 导航布局 — 5 个 Tab 对应设计稿底部导航栏
+ * 首页 / 客户 / 分析 / 报表 / 我的
+ *
+ * 使用 NativeTabs 获得原生 iOS/Android Tab Bar 体验
+ * 图标:复用已有 PNG 资源,TODO: 补充 analytics / reports / profile 专属图标
+ */
+export default function TabLayout() {
+  const scheme = useColorScheme();
+  const colors = Colors[scheme === 'unspecified' ? 'light' : scheme];
+  const { isAuthenticated } = useAuth();
+  useEffect(() => { 
+    if (!isAuthenticated) {
+      router.push('/sign-in');
+    }
+  }, [isAuthenticated]);
+  return (
+    <NativeTabs
+      backgroundColor={colors.background}
+      indicatorColor={colors.backgroundElement}
+      labelStyle={{ selected: { color: colors.text } }}
+    >
+      <NativeTabs.Trigger name="index">
+        <NativeTabs.Trigger.Label>首页</NativeTabs.Trigger.Label>
+        <NativeTabs.Trigger.Icon
+          src={require('@/assets/images/tabIcons/home.png')}
+          renderingMode="template"
+        />
+      </NativeTabs.Trigger>
+
+      <NativeTabs.Trigger name="customer">
+        <NativeTabs.Trigger.Label>客户</NativeTabs.Trigger.Label>
+        <NativeTabs.Trigger.Icon
+          src={require('@/assets/images/tabIcons/explore.png')}
+          renderingMode="template"
+        />
+      </NativeTabs.Trigger>
+
+      <NativeTabs.Trigger name="analytics">
+        <NativeTabs.Trigger.Label>分析</NativeTabs.Trigger.Label>
+        {/* TODO: 替换为 analytics 专属图标 */}
+        <NativeTabs.Trigger.Icon
+          src={require('@/assets/images/tabIcons/explore.png')}
+          renderingMode="template"
+        />
+      </NativeTabs.Trigger>
+
+      <NativeTabs.Trigger name="reports">
+        <NativeTabs.Trigger.Label>报表</NativeTabs.Trigger.Label>
+        {/* TODO: 替换为 reports 专属图标 */}
+        <NativeTabs.Trigger.Icon
+          src={require('@/assets/images/tabIcons/explore.png')}
+          renderingMode="template"
+        />
+      </NativeTabs.Trigger>
+
+      <NativeTabs.Trigger name="profile">
+        <NativeTabs.Trigger.Label>我的</NativeTabs.Trigger.Label>
+        {/* TODO: 替换为 person 专属图标 */}
+        <NativeTabs.Trigger.Icon
+          src={require('@/assets/images/tabIcons/home.png')}
+          renderingMode="template"
+        />
+      </NativeTabs.Trigger>
+    </NativeTabs>
+  );
+}
+
+

+ 233 - 0
src/app/(tabs)/analytics.tsx

@@ -0,0 +1,233 @@
+import { PrimaryButton } from '@/components/ui/primary-button';
+import { SectionHeader } from '@/components/ui/section-header';
+import { Ionicons } from '@expo/vector-icons';
+import React, { useState } from 'react';
+import { Pressable, ScrollView, Text, View } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+/**
+ * 征信分析页 — 对应设计稿 #upload + #processing 屏幕
+ *
+ * 布局结构:
+ * 1. 标题 + 简介
+ * 2. 选择客户(下拉/输入)
+ * 3. 上传区域(点击选择文件,虚线卡片)
+ * 4. 解析设置(快速 / 深度)
+ * 5. 开始解析按钮
+ * 6. 历史解析记录
+ */
+
+interface AnalysisRecord {
+  id: string;
+  customerName: string;
+  time: string;
+  status: '解析中' | '已完成' | '解析失败';
+  progress?: number;
+  score?: string;
+}
+
+const MOCK_RECORDS: AnalysisRecord[] = [
+  { id: '1', customerName: '张德发', time: '今天 10:42', status: '已完成', score: 'B+' },
+  { id: '2', customerName: '王五', time: '今天 09:16', status: '解析中', progress: 72 },
+  { id: '3', customerName: '李美华', time: '昨天 15:20', status: '已完成', score: 'A' },
+  { id: '4', customerName: '赵丽', time: '3天前', status: '解析失败' },
+];
+
+export default function AnalyticsScreen() {
+  const [analysisMode, setAnalysisMode] = useState<'fast' | 'deep'>('fast');
+
+  return (
+    <SafeAreaView className="flex-1 bg-surface" edges={['top']}>
+      <ScrollView
+        className="flex-1"
+        contentContainerClassName="px-6 pt-4 pb-24"
+        showsVerticalScrollIndicator={false}
+      >
+        {/* ── 标题 ── */}
+        <Text className="text-3xl font-extrabold tracking-tight text-on-surface mb-2">
+          征信分析
+        </Text>
+        <Text className="text-on-surface-variant text-sm mb-8">
+          上传征信报告,AI 自动解析评估信用状况
+        </Text>
+
+        {/* ── 选择客户 ── */}
+        <View className="bg-surface-container-lowest rounded-xl p-5 shadow-sm mb-4">
+          <Text className="text-xs font-bold tracking-wider text-outline uppercase mb-3 ml-1">
+            选择客户
+          </Text>
+          <Pressable className="flex-row items-center bg-surface-container-low rounded-lg px-4 py-3.5">
+            <Ionicons name="person-outline" size={18} color="#737686" />
+            <Text className="flex-1 ml-3 text-on-surface font-medium">
+              张德发(138****8888)
+            </Text>
+            <Ionicons name="chevron-down" size={18} color="#c3c6d7" />
+          </Pressable>
+        </View>
+
+        {/* ── 上传区域 ── */}
+        <View className="bg-surface-container-lowest rounded-xl p-5 shadow-sm mb-4">
+          <Text className="text-xs font-bold tracking-wider text-outline uppercase mb-3 ml-1">
+            上传征信文件
+          </Text>
+          <Pressable className="items-center justify-center py-10 rounded-xl border-2 border-dashed border-outline-variant/40 bg-surface-container-low/50">
+            <View className="w-16 h-16 rounded-full bg-primary-fixed items-center justify-center mb-4">
+              <Ionicons name="cloud-upload-outline" size={32} color="#004ac6" />
+            </View>
+            <Text className="text-on-surface font-semibold mb-1">点击上传征信报告</Text>
+            <Text className="text-on-surface-variant text-xs">
+              支持 PDF、图片格式,最大 20MB
+            </Text>
+          </Pressable>
+        </View>
+
+        {/* ── 解析模式 ── */}
+        <View className="bg-surface-container-lowest rounded-xl p-5 shadow-sm mb-6">
+          <Text className="text-xs font-bold tracking-wider text-outline uppercase mb-3 ml-1">
+            解析模式
+          </Text>
+          <View className="flex-row gap-3">
+            <Pressable
+              onPress={() => setAnalysisMode('fast')}
+              className={`flex-1 p-4 rounded-xl items-center ${
+                analysisMode === 'fast'
+                  ? 'bg-primary-container'
+                  : 'bg-surface-container-low'
+              }`}
+            >
+              <Ionicons
+                name="flash-outline"
+                size={24}
+                color={analysisMode === 'fast' ? '#ffffff' : '#737686'}
+              />
+              <Text
+                className={`text-sm font-bold mt-2 ${
+                  analysisMode === 'fast' ? 'text-on-primary' : 'text-on-surface'
+                }`}
+              >
+                快速解析
+              </Text>
+              <Text
+                className={`text-[10px] mt-1 ${
+                  analysisMode === 'fast' ? 'text-on-primary/70' : 'text-on-surface-variant'
+                }`}
+              >
+                约30秒,关键指标
+              </Text>
+            </Pressable>
+            <Pressable
+              onPress={() => setAnalysisMode('deep')}
+              className={`flex-1 p-4 rounded-xl items-center ${
+                analysisMode === 'deep'
+                  ? 'bg-primary-container'
+                  : 'bg-surface-container-low'
+              }`}
+            >
+              <Ionicons
+                name="layers-outline"
+                size={24}
+                color={analysisMode === 'deep' ? '#ffffff' : '#737686'}
+              />
+              <Text
+                className={`text-sm font-bold mt-2 ${
+                  analysisMode === 'deep' ? 'text-on-primary' : 'text-on-surface'
+                }`}
+              >
+                深度分析
+              </Text>
+              <Text
+                className={`text-[10px] mt-1 ${
+                  analysisMode === 'deep' ? 'text-on-primary/70' : 'text-on-surface-variant'
+                }`}
+              >
+                约90秒,全面报告
+              </Text>
+            </Pressable>
+          </View>
+        </View>
+
+        {/* ── 开始解析按钮 ── */}
+        <PrimaryButton title="开始解析" className="mb-10" />
+
+        {/* ── 历史解析记录 ── */}
+        <SectionHeader title="解析记录" actionText="查看全部" />
+        <View className="gap-3">
+          {MOCK_RECORDS.map((record) => (
+            <Pressable
+              key={record.id}
+              className="bg-surface-container-lowest p-4 rounded-xl shadow-sm flex-row items-center active:bg-surface-container-low"
+            >
+              {/* 左侧图标 */}
+              <View
+                className={`w-10 h-10 rounded-full items-center justify-center mr-4 ${
+                  record.status === '已完成'
+                    ? 'bg-green-50'
+                    : record.status === '解析中'
+                      ? 'bg-blue-50'
+                      : 'bg-error-container'
+                }`}
+              >
+                <Ionicons
+                  name={
+                    record.status === '已完成'
+                      ? 'checkmark-circle'
+                      : record.status === '解析中'
+                        ? 'hourglass-outline'
+                        : 'alert-circle'
+                  }
+                  size={20}
+                  color={
+                    record.status === '已完成'
+                      ? '#16a34a'
+                      : record.status === '解析中'
+                        ? '#2563eb'
+                        : '#ba1a1a'
+                  }
+                />
+              </View>
+
+              {/* 中间内容 */}
+              <View className="flex-1">
+                <View className="flex-row items-center justify-between mb-1">
+                  <Text className="font-bold text-on-surface">{record.customerName}</Text>
+                  <Text className="text-[10px] text-on-surface-variant">{record.time}</Text>
+                </View>
+                {record.status === '解析中' && record.progress != null ? (
+                  <View>
+                    <View className="flex-row items-center justify-between mb-1">
+                      <Text className="text-xs text-on-surface-variant">解析中...</Text>
+                      <Text className="text-xs text-primary font-bold">{record.progress}%</Text>
+                    </View>
+                    <View className="h-1.5 w-full bg-surface-container rounded-full overflow-hidden">
+                      <View
+                        className="h-full bg-primary-container rounded-full"
+                        style={{ width: `${record.progress}%` }}
+                      />
+                    </View>
+                  </View>
+                ) : record.status === '已完成' ? (
+                  <View className="flex-row items-center gap-2">
+                    <Text className="text-xs text-on-surface-variant">征信评分</Text>
+                    <View className="bg-primary-fixed px-2 py-0.5 rounded-full">
+                      <Text className="text-[10px] font-bold text-primary">{record.score}</Text>
+                    </View>
+                  </View>
+                ) : (
+                  <Text className="text-xs text-error">解析失败,请重试</Text>
+                )}
+              </View>
+
+              {/* 右侧箭头 */}
+              <Ionicons
+                name="chevron-forward"
+                size={18}
+                color="#c3c6d7"
+                style={{ marginLeft: 8 }}
+              />
+            </Pressable>
+          ))}
+        </View>
+      </ScrollView>
+    </SafeAreaView>
+  );
+}

+ 233 - 0
src/app/(tabs)/customer.tsx

@@ -0,0 +1,233 @@
+import { StatusBadge } from '@/components/ui/status-badge';
+import { Ionicons } from '@expo/vector-icons';
+import React, { useState } from 'react';
+import { FlatList, Pressable, ScrollView, Text, TextInput, View } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+/**
+ * 客户管理页 — 对应设计稿 #customers 屏幕
+ *
+ * 布局结构:
+ * 1. 顶部搜索栏(filled 风格输入框)
+ * 2. 状态筛选 Tag 行(横向滚动)
+ * 3. 客户卡片列表(name / phone / status / 操作按钮)
+ * 4. 底部新增客户 FAB
+ */
+
+type CustomerStatus = '全部' | '待上传' | '已分析' | '已匹配' | '审核中';
+
+interface Customer {
+  id: string;
+  name: string;
+  phone: string;
+  loanType: string;
+  status: CustomerStatus;
+  score?: string;
+  lastUpdate: string;
+}
+
+const MOCK_CUSTOMERS: Customer[] = [
+  {
+    id: '1',
+    name: '张德发',
+    phone: '138****8888',
+    loanType: '经营贷',
+    status: '审核中',
+    score: 'B+',
+    lastUpdate: '今天 10:42',
+  },
+  {
+    id: '2',
+    name: '李美华',
+    phone: '139****6666',
+    loanType: '消费贷',
+    status: '已匹配',
+    score: 'A',
+    lastUpdate: '昨天 15:20',
+  },
+  {
+    id: '3',
+    name: '王大锤',
+    phone: '137****3333',
+    loanType: '房抵贷',
+    status: '已分析',
+    score: 'B',
+    lastUpdate: '3天前',
+  },
+  {
+    id: '4',
+    name: '赵丽',
+    phone: '136****1111',
+    loanType: '消费贷',
+    status: '待上传',
+    lastUpdate: '5天前',
+  },
+  {
+    id: '5',
+    name: '钱进',
+    phone: '135****9999',
+    loanType: '经营贷',
+    status: '已匹配',
+    score: 'A-',
+    lastUpdate: '1周前',
+  },
+];
+
+const FILTERS: CustomerStatus[] = ['全部', '待上传', '已分析', '已匹配', '审核中'];
+
+const statusBadgeVariant = (s: CustomerStatus) => {
+  switch (s) {
+    case '待上传':
+      return 'error';
+    case '已分析':
+      return 'primary';
+    case '已匹配':
+      return 'success';
+    case '审核中':
+      return 'secondary';
+    default:
+      return 'secondary';
+  }
+};
+
+export default function CustomerScreen() {
+  const [search, setSearch] = useState('');
+  const [activeFilter, setActiveFilter] = useState<CustomerStatus>('全部');
+
+  const filtered = MOCK_CUSTOMERS.filter((c) => {
+    const matchSearch =
+      !search || c.name.includes(search) || c.phone.includes(search);
+    const matchFilter = activeFilter === '全部' || c.status === activeFilter;
+    return matchSearch && matchFilter;
+  });
+
+  return (
+    <SafeAreaView className="flex-1 bg-surface" edges={['top']}>
+      {/* ── 顶部标题 ── */}
+      <View className="px-6 pt-4 pb-2">
+        <Text className="text-3xl font-extrabold tracking-tight text-on-surface mb-4">
+          客户
+        </Text>
+
+        {/* 搜索栏 — filled 风格 */}
+        <View className="flex-row items-center bg-surface-container-low rounded-lg px-4 py-3 mb-4">
+          <Ionicons name="search-outline" size={18} color="#737686" />
+          <TextInput
+            className="flex-1 ml-3 text-on-surface text-sm font-medium p-0"
+            placeholder="搜索客户姓名 / 手机号"
+            placeholderTextColor="#737686"
+            value={search}
+            onChangeText={setSearch}
+          />
+          {search.length > 0 && (
+            <Pressable onPress={() => setSearch('')} hitSlop={8}>
+              <Ionicons name="close-circle" size={18} color="#c3c6d7" />
+            </Pressable>
+          )}
+        </View>
+
+        {/* 筛选 Tag 行 */}
+        <ScrollView
+          horizontal
+          showsHorizontalScrollIndicator={false}
+          contentContainerClassName="gap-2 pb-2"
+        >
+          {FILTERS.map((f) => (
+            <Pressable
+              key={f}
+              onPress={() => setActiveFilter(f)}
+              className={`px-4 py-2 rounded-full ${
+                activeFilter === f
+                  ? 'bg-primary-container'
+                  : 'bg-surface-container-lowest'
+              }`}
+            >
+              <Text
+                className={`text-xs font-bold ${
+                  activeFilter === f ? 'text-on-primary' : 'text-on-surface-variant'
+                }`}
+              >
+                {f}
+              </Text>
+            </Pressable>
+          ))}
+        </ScrollView>
+      </View>
+
+      {/* ── 客户列表 ── */}
+      <FlatList
+        data={filtered}
+        keyExtractor={(item) => item.id}
+        contentContainerClassName="px-6 pt-2 pb-24 gap-3"
+        showsVerticalScrollIndicator={false}
+        ListEmptyComponent={
+          <View className="items-center pt-20">
+            <Ionicons name="people-outline" size={48} color="#c3c6d7" />
+            <Text className="text-on-surface-variant text-sm mt-4">暂无匹配客户</Text>
+          </View>
+        }
+        renderItem={({ item }) => (
+          <Pressable className="bg-surface-container-lowest p-5 rounded-xl shadow-sm active:bg-surface-container-low">
+            {/* 第一行:姓名 + 手机 */}
+            <View className="flex-row justify-between items-start mb-2">
+              <View className="flex-row items-center gap-3">
+                <View className="w-10 h-10 rounded-full bg-primary-fixed items-center justify-center">
+                  <Text className="text-primary font-bold">{item.name[0]}</Text>
+                </View>
+                <View>
+                  <Text className="font-bold text-on-surface text-base">{item.name}</Text>
+                  <Text className="text-xs text-on-surface-variant">{item.phone}</Text>
+                </View>
+              </View>
+              <StatusBadge text={item.status} variant={statusBadgeVariant(item.status)} />
+            </View>
+
+            {/* 第二行:贷款类型 + 评分 + 更新时间 */}
+            <View className="flex-row items-center gap-4 mt-2 ml-13">
+              <View className="flex-row items-center gap-1">
+                <Ionicons name="document-text-outline" size={14} color="#737686" />
+                <Text className="text-xs text-on-surface-variant">{item.loanType}</Text>
+              </View>
+              {item.score && (
+                <View className="flex-row items-center gap-1">
+                  <Ionicons name="analytics-outline" size={14} color="#737686" />
+                  <Text className="text-xs text-on-surface-variant">评分 {item.score}</Text>
+                </View>
+              )}
+              <Text className="text-[10px] text-outline ml-auto">{item.lastUpdate}</Text>
+            </View>
+
+            {/* 第三行:操作按钮 */}
+            <View className="flex-row gap-3 mt-4">
+              {item.status === '待上传' ? (
+                <Pressable className="flex-1 py-2.5 bg-primary-container rounded-lg items-center">
+                  <Text className="text-on-primary text-xs font-bold">上传征信</Text>
+                </Pressable>
+              ) : (
+                <Pressable className="flex-1 py-2.5 bg-primary-container rounded-lg items-center">
+                  <Text className="text-on-primary text-xs font-bold">查看报告</Text>
+                </Pressable>
+              )}
+              <Pressable className="flex-1 py-2.5 bg-surface-container-high rounded-lg items-center">
+                <Text className="text-on-surface text-xs font-semibold">编辑资料</Text>
+              </Pressable>
+            </View>
+          </Pressable>
+        )}
+      />
+
+      {/* ── FAB 新增客户 ── */}
+      <View className="absolute bottom-6 right-6">
+        <Pressable
+          className="w-14 h-14 rounded-full bg-primary-container items-center justify-center shadow-xl"
+          style={({ pressed }) => ({
+            opacity: pressed ? 0.8 : 1,
+            transform: [{ scale: pressed ? 0.9 : 1 }],
+          })}
+        >
+          <Ionicons name="person-add" size={24} color="#ffffff" />
+        </Pressable>
+      </View>
+    </SafeAreaView>
+  );
+}

+ 154 - 0
src/app/(tabs)/index.tsx

@@ -0,0 +1,154 @@
+import { ActionIconButton } from '@/components/ui/action-icon-button';
+import { ActivityCard } from '@/components/ui/activity-card';
+import { SectionHeader } from '@/components/ui/section-header';
+import { Ionicons } from '@expo/vector-icons';
+import React from 'react';
+import { Pressable, ScrollView, Text, View } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+/**
+ * 首页 — 还原设计稿 home/code.html
+ *
+ * 布局结构(自上而下):
+ * 1. 顶部导航栏(头像 + 标题 + 通知铃铛)
+ * 2. 问候语 + 日期
+ * 3. Bento 式统计卡片(待处理·大卡 + 进行中/成功撮合·双小卡)
+ * 4. 快捷操作四宫格
+ * 5. 最近动态列表
+ * 6. 行业洞察 Banner
+ * 7. FAB 悬浮按钮
+ */
+export default function HomeScreen() {
+  return (
+    <SafeAreaView className="flex-1 bg-surface" edges={['top']}>
+      {/* ── 顶部导航 ── */}
+      <View className="flex-row items-center justify-between px-6 h-14">
+        <View className="flex-row items-center gap-3">
+          <View className="w-10 h-10 rounded-full bg-primary-fixed items-center justify-center overflow-hidden">
+            <Ionicons name="person" size={20} color="#004ac6" />
+          </View>
+          <Text className="text-lg font-bold text-on-surface tracking-tight">贷款助手</Text>
+        </View>
+        <Pressable hitSlop={8}>
+          <Ionicons name="notifications-outline" size={24} color="#737686" />
+        </Pressable>
+      </View>
+
+      <ScrollView
+        className="flex-1"
+        contentContainerClassName="px-6 pt-4 pb-24"
+        showsVerticalScrollIndicator={false}
+      >
+        {/* ── 问候语 ── */}
+        <View className="mb-8">
+          <Text className="text-on-surface-variant text-sm tracking-wider uppercase mb-1">
+            2024年5月22日 星期三
+          </Text>
+          <Text className="text-3xl font-extrabold tracking-tight text-on-surface">
+            早上好,王经理
+          </Text>
+        </View>
+
+        {/* ── Bento 统计卡片 ── */}
+        <View className="gap-4 mb-8">
+          {/* 大卡:待处理申请 */}
+          <View className="bg-primary-container p-6 rounded-xl shadow-lg">
+            <View className="flex-row justify-between items-start mb-4">
+              <View className="bg-white/20 p-2 rounded-lg">
+                <Ionicons name="time-outline" size={24} color="#ffffff" />
+              </View>
+              <View className="bg-white/20 px-2 py-1 rounded-full">
+                <Text className="text-xs text-on-primary font-bold">+12%</Text>
+              </View>
+            </View>
+            <Text className="text-on-primary/80 text-sm mb-1">待处理申请</Text>
+            <Text className="text-4xl font-extrabold text-on-primary">24</Text>
+          </View>
+
+          {/* 双小卡行 */}
+          <View className="flex-row gap-4">
+            <View className="flex-1 bg-surface-container-lowest p-6 rounded-xl shadow-sm">
+              <Text className="text-on-surface-variant text-sm mb-1">进行中</Text>
+              <Text className="text-2xl font-bold text-on-surface">156</Text>
+              <View className="mt-4 h-1 w-full bg-surface-container rounded-full overflow-hidden">
+                <View className="h-full bg-secondary w-[65%] rounded-full" />
+              </View>
+            </View>
+            <View className="flex-1 bg-surface-container-lowest p-6 rounded-xl shadow-sm">
+              <Text className="text-on-surface-variant text-sm mb-1">成功撮合</Text>
+              <Text className="text-2xl font-bold text-on-surface">1,280</Text>
+              <View className="mt-4 h-1 w-full bg-surface-container rounded-full overflow-hidden">
+                <View className="h-full bg-tertiary-fixed-dim w-[88%] rounded-full" />
+              </View>
+            </View>
+          </View>
+        </View>
+
+        {/* ── 快捷操作 ── */}
+        <View className="mb-10">
+          <SectionHeader title="快捷操作" />
+          <View className="flex-row justify-between">
+            <ActionIconButton icon="person-add-outline" label="新增客户" />
+            <ActionIconButton icon="cloud-upload-outline" label="上传征信" />
+            <ActionIconButton icon="calculator-outline" label="额度测算" />
+            <ActionIconButton icon="document-text-outline" label="面谈记录" />
+          </View>
+        </View>
+
+        {/* ── 最近动态 ── */}
+        <View className="mb-8">
+          <SectionHeader title="最近动态" actionText="查看全部" />
+          <View className="gap-3">
+            <ActivityCard
+              name="张德发"
+              loanType="经营贷"
+              time="10分钟前"
+              description="申请材料已上传,等待风控初审"
+              badgeText="审核中"
+              badgeVariant="secondary"
+            />
+            <ActivityCard
+              name="李美华"
+              loanType="消费贷"
+              time="2小时前"
+              description="系统匹配到 3 个符合条件的银行产品"
+              badgeText="已匹配"
+              badgeVariant="tertiary"
+            />
+            <ActivityCard
+              name="王大锤"
+              loanType="房抵贷"
+              time="1天前"
+              description="银行面谈已完成,正在落实抵押登记"
+              badgeText="待抵押"
+              badgeVariant="primary"
+            />
+          </View>
+        </View>
+
+        {/* ── 行业洞察 Banner ── */}
+        <View className="rounded-xl overflow-hidden bg-inverse-surface h-40 mb-6">
+          <View className="flex-1 p-8 justify-end z-10">
+            <Text className="text-on-primary text-xl font-bold mb-2">行业洞察</Text>
+            <Text className="text-inverse-on-surface/70 text-sm mb-4">
+              本周 LPR 利率下调,建议优先推荐...
+            </Text>
+            <Pressable className="bg-surface-container-lowest px-4 py-2 rounded-lg self-start">
+              <Text className="text-inverse-surface text-xs font-bold">立即了解</Text>
+            </Pressable>
+          </View>
+        </View>
+      </ScrollView>
+
+      {/* ── FAB 悬浮按钮 ── */}
+      <View className="absolute bottom-6 right-6">
+        <Pressable
+          className="w-14 h-14 rounded-full bg-primary-container items-center justify-center shadow-xl"
+          style={({ pressed }) => ({ opacity: pressed ? 0.8 : 1, transform: [{ scale: pressed ? 0.9 : 1 }] })}
+        >
+          <Ionicons name="add" size={28} color="#ffffff" />
+        </Pressable>
+      </View>
+    </SafeAreaView>
+  );
+}

+ 126 - 0
src/app/(tabs)/profile.tsx

@@ -0,0 +1,126 @@
+import { MenuRow } from '@/components/ui/menu-row';
+import { Ionicons } from '@expo/vector-icons';
+import React from 'react';
+import { Pressable, ScrollView, Text, View } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+/**
+ * 个人中心 — 还原设计稿 profile/code.html
+ *
+ * 布局结构:
+ * 1. 顶部导航栏
+ * 2. 头像 + 姓名 + 职位
+ * 3. 三栏统计(已完成 / 成功率 / 本月收入)
+ * 4. 菜单分组(业务 / 系统 / 退出登录)
+ */
+export default function ProfileScreen() {
+  return (
+    <SafeAreaView className="flex-1 bg-surface" edges={['top']}>
+      {/* ── 顶部导航 ── */}
+      <View className="flex-row items-center justify-between px-6 h-14">
+        <View className="flex-row items-center gap-3">
+          <View className="w-8 h-8 rounded-full bg-primary-fixed items-center justify-center overflow-hidden">
+            <Ionicons name="person" size={16} color="#004ac6" />
+          </View>
+          <Text className="text-lg font-bold text-on-surface tracking-tight">贷款助手</Text>
+        </View>
+        <Pressable hitSlop={8}>
+          <Ionicons name="notifications-outline" size={24} color="#737686" />
+        </Pressable>
+      </View>
+
+      <ScrollView
+        className="flex-1"
+        contentContainerClassName="px-6 pt-4 pb-32"
+        showsVerticalScrollIndicator={false}
+      >
+        {/* ── 头像区域 ── */}
+        <View className="items-center mb-10">
+          <View className="relative mb-4">
+            {/* 渐变圆环 — 用 primary-container 模拟 */}
+            <View className="w-28 h-28 rounded-full bg-primary-container p-1">
+              <View className="w-full h-full rounded-full overflow-hidden border-4 border-surface-container-lowest bg-surface-container items-center justify-center">
+                {/* 占位头像:实际项目替换为用户真实头像 */}
+                <Ionicons name="person" size={40} color="#495c95" />
+              </View>
+            </View>
+            {/* 认证徽章 */}
+            <View className="absolute bottom-1 right-1 bg-primary w-8 h-8 rounded-full items-center justify-center border-4 border-surface-container-lowest shadow-lg">
+              <Ionicons name="checkmark" size={14} color="#ffffff" />
+            </View>
+          </View>
+          <Text className="text-2xl font-extrabold tracking-tight text-on-surface mb-1">
+            张建华
+          </Text>
+          <Text className="text-on-surface-variant text-sm font-medium tracking-wide">
+            高级信贷经理 · 智融金融
+          </Text>
+        </View>
+
+        {/* ── 三栏统计 ── */}
+        <View className="flex-row gap-4 mb-10">
+          <View className="flex-1 bg-surface-container-lowest rounded-xl p-4 items-center shadow-sm">
+            <Text className="text-primary text-xl font-extrabold mb-1">128</Text>
+            <Text className="text-[10px] uppercase tracking-widest text-outline font-bold">
+              已完成
+            </Text>
+          </View>
+          <View className="flex-1 bg-surface-container-lowest rounded-xl p-4 items-center shadow-sm">
+            <Text className="text-primary text-xl font-extrabold mb-1">94%</Text>
+            <Text className="text-[10px] uppercase tracking-widest text-outline font-bold">
+              成功率
+            </Text>
+          </View>
+          <View className="flex-1 bg-surface-container-lowest rounded-xl p-4 items-center shadow-sm">
+            <Text className="text-primary text-xl font-extrabold mb-1">¥42k</Text>
+            <Text className="text-[10px] uppercase tracking-widest text-outline font-bold">
+              本月收入
+            </Text>
+          </View>
+        </View>
+
+        {/* ── 菜单分组 1:业务 ── */}
+        <View className="bg-surface-container-lowest rounded-xl overflow-hidden shadow-sm mb-6">
+          <MenuRow icon="people-outline" label="我的客户" iconBgClass="bg-blue-50" />
+          <MenuRow
+            icon="business-outline"
+            label="机构设置"
+            iconBgClass="bg-indigo-50"
+            iconColorClass="#4f46e5"
+          />
+        </View>
+
+        {/* ── 菜单分组 2:系统 ── */}
+        <View className="bg-surface-container-lowest rounded-xl overflow-hidden shadow-sm mb-6">
+          <MenuRow
+            icon="shield-outline"
+            label="安全设置"
+            iconBgClass="bg-orange-50"
+            iconColorClass="#ea580c"
+          />
+          <MenuRow
+            icon="help-circle-outline"
+            label="帮助与支持"
+            iconBgClass="bg-green-50"
+            iconColorClass="#16a34a"
+          />
+          <MenuRow
+            icon="information-circle-outline"
+            label="关于我们"
+            iconBgClass="bg-slate-100"
+            iconColorClass="#475569"
+          />
+        </View>
+
+        {/* ── 退出登录 ── */}
+        <Pressable
+          className="mt-4 bg-surface-container-low py-4 rounded-xl flex-row items-center justify-center gap-2"
+          style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1, transform: [{ scale: pressed ? 0.98 : 1 }] })}
+        >
+          <Ionicons name="log-out-outline" size={20} color="#ba1a1a" />
+          <Text className="text-error font-bold">退出登录</Text>
+        </Pressable>
+      </ScrollView>
+    </SafeAreaView>
+  );
+}

+ 295 - 0
src/app/(tabs)/reports.tsx

@@ -0,0 +1,295 @@
+import { StatusBadge } from '@/components/ui/status-badge';
+import { Ionicons } from '@expo/vector-icons';
+import React, { useState } from 'react';
+import { Pressable, ScrollView, Text, View } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+/**
+ * 报表页 — 对应设计稿 #reports / #report / #match 屏幕
+ *
+ * 布局结构:
+ * 1. 标题
+ * 2. 概览统计卡片(征信分析总量 / 匹配成功率 / 平均额度)
+ * 3. 筛选 Tab(全部 / 征信报告 / 匹配结果)
+ * 4. 报告卡片列表
+ */
+
+type ReportTab = '全部' | '征信报告' | '匹配结果';
+
+interface Report {
+  id: string;
+  customerName: string;
+  type: '征信报告' | '匹配结果';
+  time: string;
+  status: '已完成' | '解析中' | '待匹配';
+  score?: string;
+  matchCount?: number;
+  matchRate?: string;
+  /** 关键标签 */
+  tags?: { text: string; variant: 'primary' | 'secondary' | 'tertiary' | 'error' }[];
+}
+
+const MOCK_REPORTS: Report[] = [
+  {
+    id: '1',
+    customerName: '张德发',
+    type: '征信报告',
+    time: '今天 10:42',
+    status: '已完成',
+    score: 'B+',
+    tags: [
+      { text: '收入稳定', variant: 'primary' },
+      { text: '查询偏多', variant: 'tertiary' },
+      { text: '负债率偏高', variant: 'error' },
+    ],
+  },
+  {
+    id: '2',
+    customerName: '张德发',
+    type: '匹配结果',
+    time: '今天 11:05',
+    status: '已完成',
+    matchCount: 3,
+    matchRate: '92%',
+  },
+  {
+    id: '3',
+    customerName: '李美华',
+    type: '征信报告',
+    time: '昨天 15:20',
+    status: '已完成',
+    score: 'A',
+    tags: [
+      { text: '信用优秀', variant: 'primary' },
+      { text: '负债较低', variant: 'secondary' },
+    ],
+  },
+  {
+    id: '4',
+    customerName: '王大锤',
+    type: '征信报告',
+    time: '3天前',
+    status: '已完成',
+    score: 'B',
+    tags: [{ text: '收入稳定', variant: 'primary' }],
+  },
+  {
+    id: '5',
+    customerName: '王五',
+    type: '征信报告',
+    time: '今天 09:16',
+    status: '解析中',
+  },
+  {
+    id: '6',
+    customerName: '赵丽',
+    type: '匹配结果',
+    time: '5天前',
+    status: '待匹配',
+  },
+];
+
+const TABS: ReportTab[] = ['全部', '征信报告', '匹配结果'];
+
+export default function ReportsScreen() {
+  const [activeTab, setActiveTab] = useState<ReportTab>('全部');
+
+  const filtered = MOCK_REPORTS.filter(
+    (r) => activeTab === '全部' || r.type === activeTab
+  );
+
+  const completedCount = MOCK_REPORTS.filter((r) => r.status === '已完成').length;
+
+  return (
+    <SafeAreaView className="flex-1 bg-surface" edges={['top']}>
+      <ScrollView
+        className="flex-1"
+        contentContainerClassName="pb-24"
+        showsVerticalScrollIndicator={false}
+        stickyHeaderIndices={[2]}
+      >
+        {/* ── 标题 ── */}
+        <View className="px-6 pt-4 mb-6">
+          <Text className="text-3xl font-extrabold tracking-tight text-on-surface mb-2">
+            报表
+          </Text>
+          <Text className="text-on-surface-variant text-sm">
+            征信分析报告与智能匹配结果汇总
+          </Text>
+        </View>
+
+        {/* ── 概览统计 ── */}
+        <View className="flex-row gap-3 px-6 mb-6">
+          <View className="flex-1 bg-surface-container-lowest p-4 rounded-xl shadow-sm items-center">
+            <Text className="text-2xl font-extrabold text-primary mb-1">
+              {MOCK_REPORTS.length}
+            </Text>
+            <Text className="text-[10px] uppercase tracking-widest text-outline font-bold">
+              总报告数
+            </Text>
+          </View>
+          <View className="flex-1 bg-surface-container-lowest p-4 rounded-xl shadow-sm items-center">
+            <Text className="text-2xl font-extrabold text-primary mb-1">
+              {completedCount}
+            </Text>
+            <Text className="text-[10px] uppercase tracking-widest text-outline font-bold">
+              已完成
+            </Text>
+          </View>
+          <View className="flex-1 bg-surface-container-lowest p-4 rounded-xl shadow-sm items-center">
+            <Text className="text-2xl font-extrabold text-primary mb-1">92%</Text>
+            <Text className="text-[10px] uppercase tracking-widest text-outline font-bold">
+              最高匹配
+            </Text>
+          </View>
+        </View>
+
+        {/* ── 筛选 Tab ── */}
+        <View className="bg-surface px-6 pb-3 pt-1">
+          <View className="flex-row bg-surface-container-low rounded-lg p-1">
+            {TABS.map((tab) => (
+              <Pressable
+                key={tab}
+                onPress={() => setActiveTab(tab)}
+                className={`flex-1 py-2.5 rounded-md items-center ${
+                  activeTab === tab ? 'bg-surface-container-lowest shadow-sm' : ''
+                }`}
+              >
+                <Text
+                  className={`text-xs font-bold ${
+                    activeTab === tab ? 'text-primary' : 'text-on-surface-variant'
+                  }`}
+                >
+                  {tab}
+                </Text>
+              </Pressable>
+            ))}
+          </View>
+        </View>
+
+        {/* ── 报告列表 ── */}
+        <View className="px-6 pt-2 gap-3">
+          {filtered.length === 0 ? (
+            <View className="items-center pt-20">
+              <Ionicons name="document-text-outline" size={48} color="#c3c6d7" />
+              <Text className="text-on-surface-variant text-sm mt-4">暂无报告</Text>
+            </View>
+          ) : (
+            filtered.map((report) => (
+              <Pressable
+                key={report.id}
+                className="bg-surface-container-lowest p-5 rounded-xl shadow-sm active:bg-surface-container-low"
+              >
+                {/* 头部:类型 + 客户名 + 时间 */}
+                <View className="flex-row items-start justify-between mb-3">
+                  <View className="flex-row items-center gap-3">
+                    <View
+                      className={`w-10 h-10 rounded-xl items-center justify-center ${
+                        report.type === '征信报告' ? 'bg-blue-50' : 'bg-green-50'
+                      }`}
+                    >
+                      <Ionicons
+                        name={
+                          report.type === '征信报告'
+                            ? 'document-text-outline'
+                            : 'git-compare-outline'
+                        }
+                        size={20}
+                        color={report.type === '征信报告' ? '#2563eb' : '#16a34a'}
+                      />
+                    </View>
+                    <View>
+                      <Text className="font-bold text-on-surface">{report.customerName}</Text>
+                      <Text className="text-[10px] text-on-surface-variant">{report.type}</Text>
+                    </View>
+                  </View>
+                  <View className="items-end">
+                    <Text className="text-[10px] text-on-surface-variant mb-1">
+                      {report.time}
+                    </Text>
+                    <StatusBadge
+                      text={report.status}
+                      variant={
+                        report.status === '已完成'
+                          ? 'success'
+                          : report.status === '解析中'
+                            ? 'secondary'
+                            : 'error'
+                      }
+                    />
+                  </View>
+                </View>
+
+                {/* 征信报告:评分 + 标签 */}
+                {report.type === '征信报告' && report.status === '已完成' && (
+                  <View>
+                    <View className="flex-row items-center gap-3 mb-3">
+                      <View className="w-14 h-14 rounded-full border-4 border-primary-fixed items-center justify-center bg-surface-container-lowest">
+                        <Text className="text-primary font-extrabold text-lg">
+                          {report.score}
+                        </Text>
+                      </View>
+                      <View className="flex-1">
+                        <Text className="text-xs text-on-surface-variant mb-2">关键标签</Text>
+                        <View className="flex-row flex-wrap gap-1.5">
+                          {report.tags?.map((tag, i) => (
+                            <StatusBadge key={i} text={tag.text} variant={tag.variant} />
+                          ))}
+                        </View>
+                      </View>
+                    </View>
+                  </View>
+                )}
+
+                {/* 匹配结果:匹配数量 + 最高匹配率 */}
+                {report.type === '匹配结果' && report.status === '已完成' && (
+                  <View className="flex-row gap-4 mt-1">
+                    <View className="flex-1 bg-surface-container-low p-3 rounded-lg items-center">
+                      <Text className="text-lg font-bold text-on-surface">
+                        {report.matchCount}
+                      </Text>
+                      <Text className="text-[10px] text-on-surface-variant">匹配产品数</Text>
+                    </View>
+                    <View className="flex-1 bg-surface-container-low p-3 rounded-lg items-center">
+                      <Text className="text-lg font-bold text-primary">{report.matchRate}</Text>
+                      <Text className="text-[10px] text-on-surface-variant">最高匹配度</Text>
+                    </View>
+                  </View>
+                )}
+
+                {/* 解析中:进度条 */}
+                {report.status === '解析中' && (
+                  <View className="mt-1">
+                    <View className="flex-row items-center justify-between mb-1">
+                      <Text className="text-xs text-on-surface-variant">正在解析...</Text>
+                      <Text className="text-xs text-primary font-bold">72%</Text>
+                    </View>
+                    <View className="h-1.5 w-full bg-surface-container rounded-full overflow-hidden">
+                      <View className="h-full bg-primary-container rounded-full w-[72%]" />
+                    </View>
+                  </View>
+                )}
+
+                {/* 底部操作 */}
+                {report.status === '已完成' && (
+                  <View className="flex-row gap-3 mt-4">
+                    <Pressable className="flex-1 py-2.5 bg-primary-container rounded-lg items-center">
+                      <Text className="text-on-primary text-xs font-bold">
+                        {report.type === '征信报告' ? '查看详情' : '查看匹配'}
+                      </Text>
+                    </Pressable>
+                    <Pressable className="flex-1 py-2.5 bg-surface-container-high rounded-lg items-center">
+                      <Text className="text-on-surface text-xs font-semibold">
+                        {report.type === '征信报告' ? '智能匹配' : '重新匹配'}
+                      </Text>
+                    </Pressable>
+                  </View>
+                )}
+              </Pressable>
+            ))
+          )}
+        </View>
+      </ScrollView>
+    </SafeAreaView>
+  );
+}

+ 17 - 4
src/app/_layout.tsx

@@ -1,18 +1,31 @@
 import '@/global.css';
 
 import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
+import { Stack } from 'expo-router';
 import React from 'react';
 import { useColorScheme } from 'react-native';
 
 import { AnimatedSplashOverlay } from '@/components/animated-icon';
-import AppTabs from '@/components/app-tabs';
+import { HUDProvider } from '@/components/ui/hud';
+import { AuthProvider } from '@/utils/auth';
 
-export default function TabLayout() {
+/**
+ * 根布局 — Stack 导航器
+ * (tabs) 为主 Tab 页面组,sign-in 为全屏模态登录页
+ */
+export default function RootLayout() {
   const colorScheme = useColorScheme();
   return (
     <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
-      <AnimatedSplashOverlay />
-      <AppTabs />
+      <HUDProvider>
+        <AuthProvider>
+          <AnimatedSplashOverlay />
+          <Stack screenOptions={{ headerShown: false }}>
+            <Stack.Screen name="(tabs)" />
+            <Stack.Screen name="sign-in" options={{ presentation: 'fullScreenModal' }} />
+          </Stack>
+        </AuthProvider>
+      </HUDProvider>
     </ThemeProvider>
   );
 }

+ 0 - 11
src/app/customer.tsx

@@ -1,11 +0,0 @@
-import { SafeAreaView } from 'react-native-safe-area-context';
-
-
-
-export default function CustomerScreen() {
-  return (
-      <SafeAreaView className='flex-1'>
-        
-      </SafeAreaView>
-  );
-}

+ 0 - 11
src/app/index.tsx

@@ -1,11 +0,0 @@
-import { SafeAreaView } from 'react-native-safe-area-context';
-
-
-
-export default function HomeScreen() {
-  return (
-      <SafeAreaView className='flex-1'>
-        
-      </SafeAreaView>
-  );
-}

+ 217 - 0
src/app/sign-in.tsx

@@ -0,0 +1,217 @@
+import useHUD from '@/components/ui/hud';
+import { InputField } from '@/components/ui/input-field';
+import { PrimaryButton } from '@/components/ui/primary-button';
+import { signIn, useAuthContext } from '@/utils/auth';
+import { Ionicons } from '@expo/vector-icons';
+import { useRoute } from '@react-navigation/native';
+import React, { useState } from 'react';
+import { KeyboardAvoidingView, Platform, Pressable, ScrollView, Text, View } from 'react-native';
+import { SafeAreaView } from 'react-native-safe-area-context';
+
+/**
+ * 登录页 — 还原设计稿 sign/code.html 布局
+ *
+ * 布局层次:
+ * 1. 背景装饰层(渐变色块 + 虚化圆形)
+ * 2. 主内容区域(logo → 标题 → 登录卡片 → 操作按钮 → 社交登录 → 协议)
+ */
+export default function SignInScreen({callback}: {callback?: (signin: boolean) => void}) {
+  const [authMode, setAuthMode] = useState<'sms' | 'password'>('sms');
+  const [agreed, setAgreed] = useState(false);
+  const [loading, setLoading] = useState(false);
+  const [setToken] = useAuthContext();
+  // get rout args
+
+  const hud = useHUD();
+  const {redirectTo} = useRoute().params as { redirectTo?: string };
+  const handleLogin = async () => {
+    // setLoading(true);
+    const l = hud.loading("登录中...");
+    // TODO: 接入登录接口
+    try {
+      const token = await signIn(1);
+      if (token) {
+        setToken(token);
+      } else {
+        throw new Error('登录失败,未获取到 token');
+      }
+    } catch (error) {
+      console.error('登录失败:', error);
+      callback?.(false);
+      return;
+    }
+    finally {
+      // setLoading(false);
+      // l.close();
+    } 
+    return;
+  };
+
+  return (
+    <SafeAreaView className="flex-1 bg-surface">
+      <KeyboardAvoidingView
+        className="flex-1"
+        behavior={Platform.OS === 'ios' ? 'padding' : undefined}
+      >
+        <ScrollView
+          className="flex-1"
+          contentContainerClassName="px-8 pt-12 pb-12"
+          keyboardShouldPersistTaps="handled"
+          showsVerticalScrollIndicator={false}
+        >
+          {/* ── 背景装饰(绝对定位) ── */}
+          <View className="absolute -top-24 -right-24 w-96 h-96 bg-primary-container/5 rounded-full" />
+          <View className="absolute top-32 -left-20 w-64 h-64 bg-secondary-container/10 rounded-full" />
+
+          {/* ── Header:Logo + 标题 ── */}
+          <View className="mb-12">
+            <View className="w-16 h-16 bg-primary-container rounded-xl items-center justify-center shadow-lg mb-6">
+              <Ionicons name="business-outline" size={32} color="#ffffff" />
+            </View>
+            <Text className="text-3xl font-bold tracking-tight text-on-surface mb-2">
+              欢迎回来
+            </Text>
+            <Text className="text-on-surface-variant font-medium">
+              登录贷款助手,开启您的财务管理之旅
+            </Text>
+          </View>
+
+          {/* ── 登录卡片 ── */}
+          <View className="bg-surface-container-lowest rounded-xl p-1 shadow-sm mb-8">
+            {/* Tab 切换 */}
+            <View className="flex-row p-1 gap-1 mb-4">
+              <Pressable
+              disabled={loading}
+                onPress={() => setAuthMode('sms')}
+                className={`flex-1 py-3 rounded-lg items-center ${
+                  authMode === 'sms'
+                    ? 'bg-surface-container-lowest shadow-sm'
+                    : ''
+                }`}
+              >
+                <Text
+                  className={`text-sm font-semibold ${
+                    authMode === 'sms' ? 'text-primary' : 'text-on-surface-variant'
+                  }`}
+                >
+                  验证码登录
+                </Text>
+              </Pressable>
+              <Pressable
+                disabled={loading}
+
+                // onPress={() => setAuthMode('password')}
+                className={`flex-1 py-3 rounded-lg items-center ${
+                  authMode === 'password'
+                    ? 'bg-surface-container-lowest shadow-sm'
+                    : ''
+                }`}
+              >
+                <Text
+                  className={`text-sm font-semibold ${
+                    authMode === 'password' ? 'text-primary' : 'text-on-surface-variant'
+                  }`}
+                >
+                  密码登录
+                </Text>
+              </Pressable>
+            </View>
+
+            {/* 表单区域 */}
+            <View className="px-3 pb-4">
+              {/* 手机号 */}
+              <InputField
+              readOnly={loading}
+                label="手机号码"
+                prefix="+86"
+                placeholder="请输入手机号"
+                keyboardType="phone-pad"
+                maxLength={11}
+                suffix={
+                  <Ionicons name="phone-portrait-outline" size={18} color="#c3c6d7" />
+                }
+              />
+
+              {/* 验证码 / 密码 */}
+              {authMode === 'sms' ? (
+                <InputField
+                readOnly={loading}  
+                  label="验证码"
+                  placeholder="请输入6位验证码"
+                  keyboardType="number-pad"
+                  maxLength={6}
+                  suffix={
+                    <Pressable hitSlop={8}>
+                      <Text className="text-sm font-bold text-primary">获取验证码</Text>
+                    </Pressable>
+                  }
+                />
+              ) : (
+                <InputField
+                    readOnly={loading}
+                  label="密码"
+                  placeholder="请输入密码"
+                  secureTextEntry
+                  suffix={
+                    <Ionicons name="eye-off-outline" size={18} color="#c3c6d7" />
+                  }
+                />
+              )}
+            </View>
+          </View>
+
+          {/* ── 操作按钮 ── */}
+          <View className="gap-4 mb-8">
+            <PrimaryButton title="立即登录" onPress={handleLogin} />
+            <View className="flex-row items-center justify-between px-2">
+              <Pressable hitSlop={8}>
+                <Text className="text-sm font-medium text-on-surface-variant">注册账号</Text>
+              </Pressable>
+              <Pressable hitSlop={8}>
+                <Text className="text-sm font-medium text-on-surface-variant">遇到问题?</Text>
+              </Pressable>
+            </View>
+          </View>
+
+          {/* ── 社交登录分割线 ── */}
+          <View className="flex-row items-center gap-4 mb-8">
+            <View className="h-px flex-1 bg-surface-container-highest" />
+            <Text className="text-xs font-bold text-outline tracking-widest uppercase">
+              其他方式登录
+            </Text>
+            <View className="h-px flex-1 bg-surface-container-highest" />
+          </View>
+
+          {/* 第三方图标 */}
+          <View className="flex-row justify-center gap-8 mb-10">
+            <Pressable className="w-12 h-12 rounded-full bg-surface-container-low items-center justify-center">
+              <Ionicons name="chatbubble-ellipses-outline" size={24} color="#737686" />
+            </Pressable>
+            <Pressable className="w-12 h-12 rounded-full bg-surface-container-low items-center justify-center">
+              <Ionicons name="logo-apple" size={24} color="#737686" />
+            </Pressable>
+          </View>
+
+          {/* ── 用户协议 ── */}
+          <View className="flex-row items-start gap-3 px-2">
+            <Pressable onPress={() => setAgreed(!agreed)} className="pt-0.5">
+              <View
+                className={`w-5 h-5 rounded-full border-2 items-center justify-center ${
+                  agreed ? 'bg-primary border-primary' : 'border-outline-variant bg-surface-container-low'
+                }`}
+              >
+                {agreed && <Ionicons name="checkmark" size={12} color="#fff" />}
+              </View>
+            </Pressable>
+            <Text className="text-xs leading-relaxed text-on-surface-variant flex-1">
+              登录即代表您已阅读并同意
+              <Text className="text-primary font-semibold">《用户服务协议》</Text>、
+              <Text className="text-primary font-semibold">《隐私政策》</Text>
+              以及授权该应用获取您的公开信息。
+            </Text>
+          </View>
+        </ScrollView>
+      </KeyboardAvoidingView>
+    </SafeAreaView>
+  );
+}

+ 1 - 1
src/components/animated-icon.tsx

@@ -125,7 +125,7 @@ const styles = StyleSheet.create({
     position: 'absolute',
   },
   backgroundSolidColor: {
-    ...StyleSheet.absoluteFillObject,
+    ...StyleSheet.absoluteFill,
     backgroundColor: '#208AEF',
     zIndex: 1000,
   },

+ 25 - 0
src/components/ui/action-icon-button.tsx

@@ -0,0 +1,25 @@
+import { Ionicons } from '@expo/vector-icons';
+import React from 'react';
+import { Pressable, Text, View } from 'react-native';
+
+interface ActionIconButtonProps {
+  icon: keyof typeof Ionicons.glyphMap;
+  label: string;
+  onPress?: () => void;
+}
+
+/** 快捷操作按钮 — 圆角图标 + 底部文字 */
+export function ActionIconButton({ icon, label, onPress }: ActionIconButtonProps) {
+  return (
+    <Pressable
+      onPress={onPress}
+      className="items-center gap-2"
+      style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1, transform: [{ scale: pressed ? 0.95 : 1 }] })}
+    >
+      <View className="w-14 h-14 rounded-2xl bg-surface-container-high items-center justify-center">
+        <Ionicons name={icon} size={24} color="#004ac6" />
+      </View>
+      <Text className="text-xs font-medium text-on-surface-variant">{label}</Text>
+    </Pressable>
+  );
+}

+ 63 - 0
src/components/ui/activity-card.tsx

@@ -0,0 +1,63 @@
+import { Image } from 'expo-image';
+import type { ComponentProps } from 'react';
+import React from 'react';
+import { Pressable, Text, View } from 'react-native';
+import { StatusBadge } from './status-badge';
+
+interface ActivityCardProps {
+  avatarUri?: string;
+  name: string;
+  loanType: string;
+  time: string;
+  description: string;
+  badgeText: string;
+  badgeVariant?: ComponentProps<typeof StatusBadge>['variant'];
+  onPress?: () => void;
+}
+
+/**
+ * 动态卡片 — 展示客户最近操作动态
+ * surface-container-lowest 底色 + shadow-sm 微浮
+ */
+export function ActivityCard({
+  avatarUri,
+  name,
+  loanType,
+  time,
+  description,
+  badgeText,
+  badgeVariant = 'secondary',
+  onPress,
+}: ActivityCardProps) {
+  return (
+    <Pressable
+      onPress={onPress}
+      className="bg-surface-container-lowest p-4 rounded-xl flex-row items-center gap-4 shadow-sm"
+    >
+      {/* 头像 */}
+      <View className="w-12 h-12 rounded-full overflow-hidden bg-surface-container">
+        {avatarUri ? (
+          <Image source={{ uri: avatarUri }} className="w-full h-full" contentFit="cover" />
+        ) : (
+          <View className="w-full h-full bg-secondary-container items-center justify-center">
+            <Text className="text-on-secondary-container font-bold">{name[0]}</Text>
+          </View>
+        )}
+      </View>
+      {/* 正文 */}
+      <View className="flex-1">
+        <View className="flex-row justify-between items-start mb-1">
+          <Text className="font-bold text-on-surface" numberOfLines={1}>
+            {name} - {loanType}
+          </Text>
+          <Text className="text-[10px] text-on-surface-variant ml-2">{time}</Text>
+        </View>
+        <Text className="text-xs text-on-surface-variant" numberOfLines={1}>
+          {description}
+        </Text>
+      </View>
+      {/* 状态 */}
+      <StatusBadge text={badgeText} variant={badgeVariant} />
+    </Pressable>
+  );
+}

+ 35 - 0
src/components/ui/bento-feature.tsx

@@ -0,0 +1,35 @@
+import { Ionicons } from '@expo/vector-icons';
+import React from 'react';
+import { Text, View } from 'react-native';
+
+interface BentoFeatureProps {
+  icon: keyof typeof Ionicons.glyphMap;
+  iconBgClass?: string;
+  iconColor?: string;
+  label: string;
+  value: string;
+}
+
+/**
+ * Bento 特性卡片 — 小卡片展示关键指标
+ * surface-container-lowest 底色,xl 圆角
+ */
+export function BentoFeature({
+  icon,
+  iconBgClass = 'bg-blue-50',
+  iconColor = '#2563eb',
+  label,
+  value,
+}: BentoFeatureProps) {
+  return (
+    <View className="flex-1 bg-surface-container-lowest p-5 rounded-2xl justify-between h-36 shadow-sm">
+      <View className={`w-10 h-10 rounded-xl items-center justify-center ${iconBgClass}`}>
+        <Ionicons name={icon} size={22} color={iconColor} />
+      </View>
+      <View className="mt-2">
+        <Text className="text-xs text-on-surface-variant">{label}</Text>
+        <Text className="font-bold text-lg text-on-surface">{value}</Text>
+      </View>
+    </View>
+  );
+}

+ 77 - 0
src/components/ui/confirm-dialog.tsx

@@ -0,0 +1,77 @@
+import { Ionicons } from '@expo/vector-icons';
+import React from 'react';
+import { Pressable, Text, View } from 'react-native';
+
+interface ConfirmDialogProps {
+  icon?: keyof typeof Ionicons.glyphMap;
+  title: string;
+  subtitle?: string;
+  /** 详情键值对 */
+  details?: { label: string; value: string }[];
+  cancelText?: string;
+  confirmText: string;
+  onCancel?: () => void;
+  onConfirm?: () => void;
+}
+
+/**
+ * 确认操作对话框 — 用于放款、转账等需二次确认的场景
+ * 标题 + 详情面板 + 双按钮
+ */
+export function ConfirmDialog({
+  icon = 'wallet-outline',
+  title,
+  subtitle,
+  details,
+  cancelText = '稍后再说',
+  confirmText,
+  onCancel,
+  onConfirm,
+}: ConfirmDialogProps) {
+  return (
+    <View className="bg-surface-container-lowest p-6 rounded-xl shadow-lg">
+      {/* 头部 */}
+      <View className="flex-row items-start gap-4 mb-6">
+        <View className="p-3 bg-tertiary-fixed rounded-xl">
+          <Ionicons name={icon} size={24} color="#943700" />
+        </View>
+        <View className="flex-1">
+          <Text className="text-lg font-bold text-on-surface">{title}</Text>
+          {subtitle && (
+            <Text className="text-on-surface-variant text-sm mt-1">{subtitle}</Text>
+          )}
+        </View>
+      </View>
+
+      {/* 详情面板 */}
+      {details && details.length > 0 && (
+        <View className="bg-surface-container-low rounded-xl p-4 gap-3 mb-6">
+          {details.map((d, i) => (
+            <View key={i} className="flex-row justify-between items-center">
+              <Text className="text-sm text-on-surface-variant">{d.label}</Text>
+              <Text className="font-bold text-base text-on-surface">{d.value}</Text>
+            </View>
+          ))}
+        </View>
+      )}
+
+      {/* 按钮组 */}
+      <View className="flex-row gap-3">
+        <Pressable
+          onPress={onCancel}
+          className="flex-1 py-4 bg-surface-container-high rounded-xl items-center"
+          style={({ pressed }) => ({ opacity: pressed ? 0.8 : 1 })}
+        >
+          <Text className="font-semibold text-on-surface">{cancelText}</Text>
+        </Pressable>
+        <Pressable
+          onPress={onConfirm}
+          className="flex-1 py-4 bg-primary rounded-xl items-center shadow-md"
+          style={({ pressed }) => ({ opacity: pressed ? 0.8 : 1 })}
+        >
+          <Text className="font-semibold text-on-primary">{confirmText}</Text>
+        </Pressable>
+      </View>
+    </View>
+  );
+}

+ 266 - 0
src/components/ui/hud.tsx

@@ -0,0 +1,266 @@
+import { useTimeout } from "@/hooks/hooks";
+import { Ionicons } from "@expo/vector-icons";
+import {
+  createContext,
+  memo,
+  useCallback,
+  useContext,
+  useMemo,
+  useRef,
+  useState,
+} from "react";
+import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
+
+// ---- Types ----
+
+type HUDType = "loading" | "error" | "success" | "progress";
+
+interface HUDProps {
+  type?: HUDType;
+  /** 0-1 的数字,表示进度 */
+  progress?: number;
+  msg?: string;
+  mask?: boolean;
+  autoHide?: boolean;
+  duration?: number;
+  onHide?: () => void;
+}
+
+export type HUDOptions = HUDProps;
+
+interface HUDHandle {
+  close: () => void;
+  update: (optionsOrMsg: Partial<HUDProps> | string) => void;
+}
+
+interface ProgressHandle {
+  close: () => void;
+  update: (progress?: number, msg?: string) => void;
+}
+
+// ---- Context ----
+
+interface HUDContextValue {
+  remove: (key: string) => void;
+  add: (props: HUDProps) => string;
+  edit: (key: string, newProps: Partial<HUDProps>) => void;
+  getList: () => Record<string, HUDProps>;
+}
+
+const HUDContext = createContext<HUDContextValue | null>(null);
+
+// ---- Internal Component ----
+
+interface HUDInnerProps extends HUDProps {
+  hudKey: string;
+  close: (key: string) => void;
+}
+
+const HUDInner = memo(function HUDInner({
+  type,
+  msg,
+  progress = 0,
+  autoHide = true,
+  duration = 200,
+  hudKey,
+  onHide,
+  close,
+  mask = false,
+}: HUDInnerProps) {
+  useTimeout(() => {
+    close(hudKey);
+    onHide?.();
+  }, autoHide ? (duration ?? 2000) : null);
+  return (
+    <View
+      // entering={FadeIn}
+      // exiting={FadeOut}
+      style={[styles.overlay, { backgroundColor: 1 ? "rgba(0,0,0,0.2)" : "transparent" }]}
+      // pointerEvents={mask ? "auto" : "box-none"}
+    >
+      <View className="flex items-center justify-center bg-gray-800 rounded-lg px-5 py-4 max-w-1/2 h-auto">
+        {type === "loading" && (
+          <ActivityIndicator size="large" color="#fff" />
+        )}
+        {type === "error" && (
+          <Ionicons name="close-circle" size={48} color="red" />
+        )}
+        {type === "success" && (
+          <Ionicons name="checkmark-circle" size={48} color="green" />
+        )}
+        {type === "progress" && (
+          <View className="w-16 h-16 rounded-full border-4 border-gray-300 items-center justify-center">
+            <View
+              className="absolute w-16 h-16 rounded-full border-4 border-primary"
+              style={{
+                transform: [{ rotate: `${(progress ?? 0) * 360}deg` }],
+              }}
+            />
+          </View>
+        )}
+        {msg != null && (
+          <Text className={type ? "mt-2 text-white" : "text-white"}>
+            {msg}
+          </Text>
+        )}
+      </View>
+    </View>
+  );
+});
+
+// ---- Key Generator ----
+
+let nextKey = 0;
+
+// ---- Provider ----
+
+export function HUDProvider({ children }: { children: React.ReactNode }) {
+  const [list, setList] = useState<Record<string, HUDProps>>({});
+  const listRef = useRef(list);
+  listRef.current = list;
+
+  const remove = useCallback((key: string) => {
+    setList((prev) => {
+      if (!prev[key]) return prev;
+      const { [key]: _, ...rest } = prev;
+      return rest;
+    });
+  }, []);
+
+  const add = useCallback((props: HUDProps) => {
+    const key = `hud_${nextKey++}`;
+    setList((prev) => ({ ...prev, [key]: props }));
+    return key;
+  }, []);
+
+  const edit = useCallback((key: string, newProps: Partial<HUDProps>) => {
+    setList((prev) => {
+      if (!prev[key]) return prev;
+      return { ...prev, [key]: { ...prev[key], ...newProps } };
+    });
+  }, [setList]);
+
+  const getList = useCallback(() => listRef.current, []);
+
+  // contextValue 引用稳定,不会因 list 变化触发 consumer 重渲染
+  const contextValue = useMemo<HUDContextValue>(
+    () => ({ remove, add, edit, getList }),
+    [remove, add, edit, getList],
+  );
+
+  return (
+    <HUDContext.Provider value={contextValue}>
+      {children}
+      {Object.entries(list).map(([key, props]) => (
+        <HUDInner key={key} hudKey={key} {...props} close={remove} />
+      ))}
+    </HUDContext.Provider>
+  );
+}
+
+// ---- Hook helpers ----
+
+function createHandle(
+  key: string,
+  remove: HUDContextValue["remove"],
+  edit: HUDContextValue["edit"],
+): HUDHandle {
+  return {
+    close: () => remove(key),
+    update: (optionsOrMsg) => {
+      edit(
+        key,
+        typeof optionsOrMsg === "string" ? { msg: optionsOrMsg } : optionsOrMsg,
+      );
+    },
+  };
+}
+
+// ---- Hook ----
+
+export default function useHUD() {
+  const ctx = useContext(HUDContext);
+  if (!ctx) throw new Error("useHUD must be used within HUDProvider");
+  const { add, edit, remove, getList } = ctx;
+
+  return useMemo(() => ({
+    hideAll: () => {
+      Object.keys(getList()).forEach(remove);
+    },
+    hideAllLoading: () => {
+      Object.entries(getList()).forEach(([key, props]) => {
+        if (props.type === "loading") remove(key);
+      });
+    },
+    show: (options: HUDOptions | string): HUDHandle => {
+      const opts: HUDProps =
+        typeof options === "string"
+          ? { msg: options, type: "loading", autoHide: true }
+          : { autoHide: true, ...options };
+      return createHandle(add(opts), remove, edit);
+    },
+    tips: (msg: string, duration?: number): HUDHandle => {
+      return createHandle(add({ msg, autoHide: true, duration }), remove, edit);
+    },
+    success: (
+      msg?: string,
+      options?: Omit<HUDProps, "progress" | "type">,
+    ): HUDHandle => {
+      return createHandle(
+        add({ type: "success", msg, autoHide: true, ...options }),
+        remove,
+        edit,
+      );
+    },
+    error: (
+      msg?: string,
+      options?: Omit<HUDProps, "progress" | "type">,
+    ): HUDHandle => {
+      return createHandle(
+        add({ type: "error", msg, autoHide: true, ...options }),
+        remove,
+        edit,
+      );
+    },
+    loading: (msg?: string, options?: Omit<HUDProps, "type">): HUDHandle => {
+      return createHandle(
+        add({ autoHide: false, mask: true, ...options, type: "loading", msg }),
+        remove,
+        edit,
+      );
+    },
+    progress: (
+      options?: Omit<HUDProps, "type" | "progress">,
+    ): ProgressHandle => {
+      const key = add({
+        mask: true,
+        ...options,
+        type: "progress",
+        progress: 0,
+        autoHide: false,
+      });
+      return {
+        close: () => remove(key),
+        update: (progress?: number, msg?: string) => {
+          edit(key, { progress, msg });
+        },
+      };
+    },
+  }), [add, edit, remove, getList]);
+}
+
+export type { HUDHandle, HUDProps, ProgressHandle };
+
+const styles = StyleSheet.create({
+  overlay: {
+    position: "absolute",
+    left: 0,
+    top: 0,
+    width: "100%",
+    height: "100%",
+    paddingBottom: '15%',
+    zIndex: 50,
+    alignItems: "center",
+    justifyContent: "center",
+  },
+});

+ 50 - 0
src/components/ui/input-field.tsx

@@ -0,0 +1,50 @@
+import React, { useState } from 'react';
+import { Text, TextInput, View, type TextInputProps } from 'react-native';
+
+interface InputFieldProps extends TextInputProps {
+  label?: string;
+  /** 输入框左侧前缀文字,如 "+86" */
+  prefix?: string;
+  /** 输入框右侧操作按钮 */
+  suffix?: React.ReactNode;
+}
+
+/**
+ * 填充式输入框 — 遵循 "Ethereal Interface" 设计规范:
+ * surface-container-low 底色,聚焦时切换为 surface-container-lowest + primary 微光边框
+ */
+export function InputField({ label, prefix, suffix, ...inputProps }: InputFieldProps) {
+  const [focused, setFocused] = useState(false);
+
+  return (
+    <View className="mb-4">
+      {label && (
+        <Text className="text-xs font-bold tracking-wider text-outline uppercase mb-2 ml-1">
+          {label}
+        </Text>
+      )}
+      <View
+        className={`flex-row items-center px-4 py-3.5 rounded-lg ${
+          focused
+            ? 'bg-surface-container-lowest border-2 border-primary/20'
+            : 'bg-surface-container-low border-2 border-transparent'
+        }`}
+      >
+        {prefix && (
+          <>
+            <Text className="text-on-surface font-semibold mr-3">{prefix}</Text>
+            <View className="w-px h-4 bg-outline-variant/30 mr-3" />
+          </>
+        )}
+        <TextInput
+          className="flex-1 text-on-surface font-medium p-0 text-base"
+          placeholderTextColor="#737686"
+          onFocus={() => setFocused(true)}
+          onBlur={() => setFocused(false)}
+          {...inputProps}
+        />
+        {suffix}
+      </View>
+    </View>
+  );
+}

+ 58 - 0
src/components/ui/list-row.tsx

@@ -0,0 +1,58 @@
+import { Ionicons } from '@expo/vector-icons';
+import type { ComponentProps } from 'react';
+import React from 'react';
+import { Pressable, Text, View } from 'react-native';
+import { StatusBadge } from './status-badge';
+
+interface ListRowProps {
+  icon: keyof typeof Ionicons.glyphMap;
+  iconBgClass?: string;
+  iconColor?: string;
+  title: string;
+  subtitle?: string;
+  /** 右侧徽章 */
+  badgeText?: string;
+  badgeVariant?: ComponentProps<typeof StatusBadge>['variant'];
+  /** 自定义右侧元素(如 switch) */
+  right?: React.ReactNode;
+  onPress?: () => void;
+}
+
+/**
+ * 通用列表行 — 图标 + 标题/副标题 + 可选徽章/右侧控件
+ * 遵循 "No-Divider" 规则,通过 padding 和背景变化分隔
+ */
+export function ListRow({
+  icon,
+  iconBgClass = 'bg-blue-50',
+  iconColor = '#2563eb',
+  title,
+  subtitle,
+  badgeText,
+  badgeVariant = 'error',
+  right,
+  onPress,
+}: ListRowProps) {
+  return (
+    <Pressable
+      onPress={onPress}
+      className="flex-row items-center justify-between p-5 active:bg-surface-container-low"
+    >
+      <View className="flex-row items-center gap-4 flex-1">
+        <View className={`w-10 h-10 rounded-xl items-center justify-center ${iconBgClass}`}>
+          <Ionicons name={icon} size={22} color={iconColor} />
+        </View>
+        <View className="flex-1">
+          <Text className="font-semibold text-on-surface">{title}</Text>
+          {subtitle && (
+            <Text className="text-xs text-on-surface-variant mt-0.5">{subtitle}</Text>
+          )}
+        </View>
+      </View>
+      <View className="flex-row items-center gap-2">
+        {badgeText && <StatusBadge text={badgeText} variant={badgeVariant} />}
+        {right ?? <Ionicons name="chevron-forward" size={20} color="#737686" />}
+      </View>
+    </Pressable>
+  );
+}

+ 38 - 0
src/components/ui/menu-row.tsx

@@ -0,0 +1,38 @@
+import { Ionicons } from '@expo/vector-icons';
+import React from 'react';
+import { Pressable, Text, View } from 'react-native';
+
+interface MenuRowProps {
+  icon: keyof typeof Ionicons.glyphMap;
+  iconBgClass?: string;
+  iconColorClass?: string;
+  label: string;
+  onPress?: () => void;
+  right?: React.ReactNode;
+}
+
+/**
+ * 设置菜单行 — 左侧图标 + 标签,右侧 chevron 或自定义内容
+ * 遵循 "No-Divider" 规则,不使用分隔线
+ */
+export function MenuRow({
+  icon,
+  iconBgClass = 'bg-blue-50',
+  iconColorClass,
+  label,
+  onPress,
+  right,
+}: MenuRowProps) {
+  return (
+    <Pressable
+      onPress={onPress}
+      className="flex-row items-center px-5 py-4 active:bg-surface-container-low"
+    >
+      <View className={`w-10 h-10 rounded-lg items-center justify-center mr-4 ${iconBgClass}`}>
+        <Ionicons name={icon} size={22} color={iconColorClass ?? '#004ac6'} />
+      </View>
+      <Text className="flex-1 font-semibold text-on-surface text-base">{label}</Text>
+      {right ?? <Ionicons name="chevron-forward" size={20} color="#c3c6d7" />}
+    </Pressable>
+  );
+}

+ 25 - 0
src/components/ui/primary-button.tsx

@@ -0,0 +1,25 @@
+import React from 'react';
+import { Pressable, Text, type ViewStyle } from 'react-native';
+
+interface PrimaryButtonProps {
+  title: string;
+  onPress?: () => void;
+  className?: string;
+  style?: ViewStyle;
+}
+
+/**
+ * 主操作按钮 — 设计稿采用 primary-container → primary 135° 渐变,
+ * 此处使用纯 primary-container 底色,如需渐变可接入 expo-linear-gradient。
+ */
+export function PrimaryButton({ title, onPress, className = '' }: PrimaryButtonProps) {
+  return (
+    <Pressable
+      onPress={onPress}
+      className={`w-full py-4 bg-primary-container rounded-lg items-center justify-center shadow-md ${className}`}
+      style={({ pressed }) => ({ opacity: pressed ? 0.92 : 1, transform: [{ scale: pressed ? 0.98 : 1 }] })}
+    >
+      <Text className="text-on-primary font-bold text-base">{title}</Text>
+    </Pressable>
+  );
+}

+ 22 - 0
src/components/ui/section-header.tsx

@@ -0,0 +1,22 @@
+import React from 'react';
+import { Pressable, Text, View } from 'react-native';
+
+interface SectionHeaderProps {
+  title: string;
+  actionText?: string;
+  onAction?: () => void;
+}
+
+/** 区域标题 — 左侧加粗标题 + 右侧可选操作文字 */
+export function SectionHeader({ title, actionText, onAction }: SectionHeaderProps) {
+  return (
+    <View className="flex-row items-center justify-between mb-5">
+      <Text className="text-xl font-bold tracking-tight text-on-surface">{title}</Text>
+      {actionText && (
+        <Pressable onPress={onAction} hitSlop={8}>
+          <Text className="text-primary text-sm font-medium">{actionText}</Text>
+        </Pressable>
+      )}
+    </View>
+  );
+}

+ 27 - 0
src/components/ui/status-badge.tsx

@@ -0,0 +1,27 @@
+import React from 'react';
+import { Text, View } from 'react-native';
+
+type BadgeVariant = 'primary' | 'secondary' | 'tertiary' | 'error' | 'success';
+
+const variantClasses: Record<BadgeVariant, { bg: string; text: string }> = {
+  primary: { bg: 'bg-primary-fixed', text: 'text-on-primary-fixed' },
+  secondary: { bg: 'bg-secondary-container', text: 'text-on-secondary-container' },
+  tertiary: { bg: 'bg-tertiary-fixed', text: 'text-on-tertiary-fixed' },
+  error: { bg: 'bg-error-container', text: 'text-on-error-container' },
+  success: { bg: 'bg-green-50', text: 'text-green-700' },
+};
+
+interface StatusBadgeProps {
+  text: string;
+  variant?: BadgeVariant;
+}
+
+/** 状态标签 — 圆角药丸形,通过 variant 控制配色 */
+export function StatusBadge({ text, variant = 'secondary' }: StatusBadgeProps) {
+  const v = variantClasses[variant];
+  return (
+    <View className={`px-2.5 py-1 rounded-full ${v.bg}`}>
+      <Text className={`text-[10px] font-bold ${v.text}`}>{text}</Text>
+    </View>
+  );
+}

+ 49 - 0
src/components/ui/success-modal.tsx

@@ -0,0 +1,49 @@
+import { Ionicons } from '@expo/vector-icons';
+import React from 'react';
+import { Pressable, Text, View } from 'react-native';
+
+interface SuccessModalProps {
+  title: string;
+  message: string;
+  primaryAction: string;
+  secondaryAction?: string;
+  onPrimary?: () => void;
+  onSecondary?: () => void;
+}
+
+/**
+ * 成功/结果弹窗卡片 — iOS 居中样式
+ * 绿色圆形打钩 + 标题 + 描述 + 按钮组
+ */
+export function SuccessModal({
+  title,
+  message,
+  primaryAction,
+  secondaryAction,
+  onPrimary,
+  onSecondary,
+}: SuccessModalProps) {
+  return (
+    <View className="bg-surface-container-lowest p-8 rounded-xl items-center shadow-lg">
+      <View className="w-16 h-16 rounded-full bg-green-50 items-center justify-center mb-4">
+        <Ionicons name="checkmark-circle" size={40} color="#22c55e" />
+      </View>
+      <Text className="text-xl font-bold text-on-surface mb-2">{title}</Text>
+      <Text className="text-on-surface-variant text-sm text-center leading-relaxed mb-6">
+        {message}
+      </Text>
+      <Pressable
+        onPress={onPrimary}
+        className="w-full py-4 bg-primary-container rounded-xl items-center shadow-md mb-3"
+        style={({ pressed }) => ({ opacity: pressed ? 0.9 : 1 })}
+      >
+        <Text className="text-on-primary font-semibold">{primaryAction}</Text>
+      </Pressable>
+      {secondaryAction && (
+        <Pressable onPress={onSecondary} className="w-full py-4 items-center">
+          <Text className="text-primary font-medium">{secondaryAction}</Text>
+        </Pressable>
+      )}
+    </View>
+  );
+}

+ 24 - 0
src/components/ui/toggle-switch.tsx

@@ -0,0 +1,24 @@
+import React from 'react';
+import { Pressable, View } from 'react-native';
+
+interface ToggleSwitchProps {
+  value: boolean;
+  onValueChange?: (v: boolean) => void;
+}
+
+/** iOS 风格开关 — primary 色轨道 + 白色滑块 */
+export function ToggleSwitch({ value, onValueChange }: ToggleSwitchProps) {
+  return (
+    <Pressable
+      onPress={() => onValueChange?.(!value)}
+      className={`w-12 h-7 rounded-full px-1 justify-center ${
+        value ? 'bg-primary' : 'bg-surface-container-highest'
+      }`}
+    >
+      <View
+        className="w-5 h-5 bg-surface-container-lowest rounded-full shadow-sm"
+        style={{ alignSelf: value ? 'flex-end' : 'flex-start' }}
+      />
+    </Pressable>
+  );
+}

+ 46 - 0
src/hooks/hooks.ts

@@ -0,0 +1,46 @@
+import React from "react";
+
+type TimerHandle = ReturnType<typeof setTimeout>;
+
+function useScheduler(
+    callback: () => void,
+    delay: number | null,
+    schedule: (handler: () => void, timeout: number) => TimerHandle,
+    clear: (handle: TimerHandle) => void,
+) {
+    const handleRef = React.useRef<TimerHandle | null>(null);
+    const savedCallback = React.useRef(callback);
+
+    React.useEffect(() => {
+        savedCallback.current = callback;
+    }, [callback]);
+
+    React.useEffect(() => {
+        if (delay === null) {
+            return;
+        }
+
+        const run = () => {
+            savedCallback.current();
+        };
+
+        handleRef.current = schedule(run, delay);
+
+        return () => {
+            if (handleRef.current !== null) {
+                clear(handleRef.current);
+                handleRef.current = null;
+            }
+        };
+    }, [delay, schedule, clear]);
+}
+
+// 一个健壮,安全的 useTimeout
+export function useTimeout(callback: () => void, delay: number | null) {
+    useScheduler(callback, delay, setTimeout, clearTimeout);
+}
+
+// 一个健壮,安全的 useInterval
+export function useInterval(callback: () => void, delay: number | null) {
+    useScheduler(callback, delay, setInterval, clearInterval);
+}

+ 0 - 9
src/utils/auth.ts

@@ -1,9 +0,0 @@
-interface AccessToken {
-    accessToken: string;
-    expiresIn: number;
-    tokenType: string;
-    scope: string;
-}
-export function getAccessToken(): AccessToken |null |undefined {
-    return ;
-}

+ 69 - 0
src/utils/auth.tsx

@@ -0,0 +1,69 @@
+import { createContext, useContext } from "react";
+
+interface AccessToken {
+    accessToken: string;
+    expiresIn: number;
+    tokenType: string;
+    scope: string;
+}
+
+interface AuthContextType {
+    token: AccessToken | null | undefined;
+    isAuthenticated: boolean;
+    setToken: (token: AccessToken | null) => void;
+}
+
+let tokenCache: AccessToken | null = null;
+const AuthContext = createContext<AuthContextType | undefined>(undefined);
+
+
+export function AuthProvider({ children }: { children: React.ReactNode }) {
+    const token = getAccessToken();
+    const isAuthenticated = !!token;
+
+    const setToken = (token: AccessToken | null) => {
+        tokenCache = token;
+    };
+
+    return (
+        <AuthContext.Provider value={{ token, isAuthenticated, setToken }}>
+            {children}
+        </AuthContext.Provider>
+    );
+}
+
+export function useAuthContext() {
+    const context = useContext(AuthContext);
+    if (!context) {
+        throw new Error('useAuthContext must be used within an AuthProvider');
+    }
+    return [context.setToken];
+}
+
+
+export function getAccessToken(): AccessToken | null | undefined {
+    return tokenCache;
+}
+
+
+
+export function useAuth() {
+    const token = getAccessToken();
+    const isAuthenticated = !!token;
+    return { token, isAuthenticated };
+}
+
+
+export async function signIn(args: any) {
+    return new Promise<AccessToken>((resolve) => {
+        setTimeout(() => {
+            tokenCache = {
+                accessToken: 'mocked-access-token',
+                expiresIn: 3600,
+                tokenType: 'Bearer',
+                scope: 'read write',
+            };
+            resolve(tokenCache);
+        }, 3000);
+    });
+}

+ 96 - 3
tailwind.config.js

@@ -1,10 +1,103 @@
 /** @type {import('tailwindcss').Config} */
 module.exports = {
-  // NOTE: Update this to include the paths to all files that contain Nativewind classes.
   content: ["./src/**/*.{js,jsx,ts,tsx}"],
   presets: [require("nativewind/preset")],
   theme: {
-    extend: {},
+    extend: {
+      colors: {
+        // — Primary —
+        primary: {
+          DEFAULT: "#004ac6",
+          container: "#2563eb",
+          fixed: "#dbe1ff",
+          "fixed-dim": "#b4c5ff",
+        },
+        "on-primary": {
+          DEFAULT: "#ffffff",
+          container: "#eeefff",
+          fixed: "#00174b",
+          "fixed-variant": "#003ea8",
+        },
+        // — Secondary —
+        secondary: {
+          DEFAULT: "#495c95",
+          container: "#acbfff",
+          fixed: "#dbe1ff",
+          "fixed-dim": "#b4c5ff",
+        },
+        "on-secondary": {
+          DEFAULT: "#ffffff",
+          container: "#394c84",
+          fixed: "#00174b",
+          "fixed-variant": "#31447b",
+        },
+        // — Tertiary —
+        tertiary: {
+          DEFAULT: "#943700",
+          container: "#bc4800",
+          fixed: "#ffdbcd",
+          "fixed-dim": "#ffb596",
+        },
+        "on-tertiary": {
+          DEFAULT: "#ffffff",
+          container: "#ffede6",
+          fixed: "#360f00",
+          "fixed-variant": "#7d2d00",
+        },
+        // — Error —
+        error: {
+          DEFAULT: "#ba1a1a",
+          container: "#ffdad6",
+        },
+        "on-error": {
+          DEFAULT: "#ffffff",
+          container: "#93000a",
+        },
+        // — Surface (Tonal Architecture) —
+        surface: {
+          DEFAULT: "#f9f9fe",
+          dim: "#d9dade",
+          bright: "#f9f9fe",
+          variant: "#e2e2e7",
+          tint: "#0053db",
+          container: {
+            DEFAULT: "#ededf2",
+            lowest: "#ffffff",
+            low: "#f3f3f8",
+            high: "#e8e8ed",
+            highest: "#e2e2e7",
+          },
+        },
+        "on-surface": {
+          DEFAULT: "#1a1c1f",
+          variant: "#434655",
+        },
+        // — Outline —
+        outline: {
+          DEFAULT: "#737686",
+          variant: "#c3c6d7",
+        },
+        // — Background —
+        background: "#f9f9fe",
+        "on-background": "#1a1c1f",
+        // — Inverse —
+        "inverse-surface": "#2e3034",
+        "inverse-on-surface": "#f0f0f5",
+        "inverse-primary": "#b4c5ff",
+      },
+      borderRadius: {
+        DEFAULT: "0.25rem",
+        lg: "1rem",
+        xl: "1.5rem",
+        "2xl": "2rem",
+        full: "9999px",
+      },
+      fontFamily: {
+        headline: ["System", "PingFang SC", "sans-serif"],
+        body: ["System", "PingFang SC", "sans-serif"],
+        label: ["System", "PingFang SC", "sans-serif"],
+      },
+    },
   },
   plugins: [],
-}
+};