> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# 前端架构

> **状态**: 2026-06-06 verify(基于 `studio-website-monorepo/apps/web` 实查)
>
> **怎么读**:
>
> - **没 context**:Layer 1(30 秒)
> - **改前端代码**:Layer 1 + Layer 2(5 分钟)
> - **debug 数据流 / 认证**:Layer 3
> - **改部署 / 加环境**:Layer 4
> - **找 file:line**:页面 / Query hook / Pinia 速查表
>
> **图例**:Mental Model 配色统一 — 紫 = 外部 / 蓝 = 前端节点 / 绿 = 后端 / 黄 = 认证横切 / 灰 = 观测横切

***

## Layer 1 — Mental Model(30 秒)

> 前端是什么?跟谁通信?

<object type="image/svg+xml" data="/images/frontend-l1-mental-model.svg" style="max-width: 820px; width: 100%;">
Mental Model — Vue 3 SPA 部署在 Cloudflare Pages,所有数据走 studio-api,认证走 Cognito,观测走 Sentry
</object>

**一句话**:前端是纯 Vue 3 SPA。Cloudflare Pages 只托管静态文件,不跑业务逻辑;所有数据请求走 studio-api(AWS Lambda + Hono);认证走 Cognito + JWT。

***

## Layer 2 — 五层架构(5 分钟)

> 代码怎么分层?数据从上到下怎么流?

<object type="image/svg+xml" data="/images/frontend-l2-five-layers.svg" style="max-width: 900px; width: 100%;">
五层架构 — 应用入口 / 页面层 / 组件层 / 逻辑层 / API 层
</object>

### 各层职责速查

| 层        | 路径                                             | 数量                                        | 角色                                                                                    |
| -------- | ---------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------- |
| 1. 应用入口  | `src/main.ts` / `src/plugins/` / `src/router/` | 7 plugins · 5 guards                      | 启动 app + 路由守卫链(dateRedirect → datePreservation → commonGuard → authGuard → analytics) |
| 2. 页面层   | `src/pages/`                                   | **174**                                   | unplugin-vue-router 文件路由 + 7 nav 分组(Operational + Settings + Auth + 系统页)              |
| 3. 组件层   | `src/components/`                              | **391**                                   | 业务组件 + shadcn-vue 原子组件                                                                |
| 4. 逻辑层   | `src/composables/queries/` + `src/stores/`     | 38 hook + 10 store                        | TanStack Query 缓存 + Pinia 状态                                                          |
| 5. API 层 | `src/api/`                                     | 15 ot-api 文件 + Hono RPC + Cognito Amplify | Axios 拦截器(401 自动刷新 token,最多 3 次)                                                      |

***

## Layer 3 — 数据流时序 + 认证

> 用户访问 Dashboard 这一刻发生什么?

<object type="image/svg+xml" data="/images/frontend-l3-data-flow.svg" style="max-width: 900px; width: 100%;">
数据流时序 — 浏览器 / 路由守卫 / 页面 / TanStack Query / Axios / studio-api 6 个 actor
</object>

**关键边界**:

- **缓存命中** = 0 网络请求,直接从 TanStack Query 返回
- **缓存未命中** = Axios 自动加 `Authorization: Bearer {token}` → studio-api → 写入缓存 → Vue 反应式更新
- **401 自动刷新** = Axios 响应拦截器最多重试 3 次
- **认证**: AWS Cognito 通过 `aws-amplify`;JWT 存 `localStorage`(`auth` store)绕过 pinia-plugin-persistedstate

***

## Layer 4 — 部署

> 前端怎么部署到 3 个环境?

<object type="image/svg+xml" data="/images/frontend-l4-deploy.svg" style="max-width: 1000px; width: 100%;">
部署 — GitHub Actions 3 workflow → wrangler pages deploy → 3 个 CF Pages project / 3 个域名
</object>

**关键事实(从 `.github/workflows/` + `apps/web/wrangler.toml` verify)**:

- **纯 Wrangler CLI 部署**,不走 CF Pages git integration
- **3 个 workflow**: `deploy-test.yml`(push 到 main 自动) / `deploy-preprod.yml`(`workflow_dispatch` + push `release/pre`)/ `deploy-prod.yml`(`workflow_dispatch` 手动 + 输 "prod" 二次确认)
- **3 个 CF Pages project**: `studio-test` / `studio-pre` / `studio`
- **3 个域名**: studio-test.retaintive.ai / studio-pre.retaintive.ai / studio.retaintive.ai
- **build 命令**: `bun run build` → `wrangler pages deploy dist --project-name={env}`

***

## 页面速查(Layer 3a)

> 改某个页面 / 找页面文件位置时查。基于 `find src/pages -name "*.vue"` 实查共 174 文件。

### 认证页面(无侧边栏,blank 布局)

| 路由                                                                      | 菜单名 | 功能                                    |
| ----------------------------------------------------------------------- | --- | ------------------------------------- |
| `/auth/sign-in` · `/auth/sign-in-2`                                     | -   | Cognito 邮箱密码登录(2 个 variant 待 cleanup) |
| `/auth/sign-up`                                                         | -   | 新用户注册                                 |
| `/auth/otp` · `/auth/forgot-password`                                   | -   | OTP + 忘记密码流程                          |
| `/auth/ringcentral-callback` · `/auth/callback` · `/auth/auth-callback` | -   | RingCentral / Cognito OAuth 回调        |

### 侧边栏 Operational 分组(default 布局)

| 路由                          | 菜单名               | 功能                                                               |
| --------------------------- | ----------------- | ---------------------------------------------------------------- |
| `/dashboard`                | Dashboard         | 指标卡、图表、排行榜、转化漏斗                                                  |
| `/activity-timeline`        | Activity Timeline | 店铺活动时间线周视图(原 doc 说 `/store-activity`,实际路径是 `/activity-timeline`) |
| `/staff-timeline`           | Staff Timeline    | 员工维度通话详情 + 周排班视图                                                 |
| `/calls`                    | Call Table        | 通话分页数据表 + 桌面端并排详情面板                                              |
| `/call/:telephonySessionId` | (子页面)             | UI Manifest 驱动的动态布局、音频播放、文本同步                                    |
| `/messages`                 | Messages          | SMS / 语音邮件 / 传真,按对话分组                                            |
| `/tasks`                    | Tasks             | Lead pipeline、任务 CRUD、import、通信日志(16 sub-components)             |
| `/calendar`                 | Calendar          | 店铺排班日历(9 sub-components,逐步替代 `/store-schedule`)                  |
| `/lead-tracker`             | Lead Tracker      | 线索列表、结果编辑、通话关联                                                   |
| `/phone-activity`           | —                 | 按号码展示通话/SMS/语音邮件/线索历史                                            |

### 侧边栏 Settings 分组(default 布局)

| 路由                                                                  | 菜单名               | 功能                                            |
| ------------------------------------------------------------------- | ----------------- | --------------------------------------------- |
| `/stores` · `/stores-management`                                    | Stores & Groups   | RingCentral 连接、电话号码分配(2 个路径待统一)               |
| `/store-schedule`                                                   | Store Schedule    | 店铺周时间表(被 `/calendar` 替代中)                     |
| `/access-management`                                                | Access Management | 团队成员角色分配                                      |
| `/settings` · `/settings/connections` · `/settings/change-password` | Settings          | 账号设置多页面                                       |
| `/connections`                                                      | Connections       | 顶层 connections 页(区别于 `/settings/connections`) |
| `/billing` · `/billing-history` · `/billing-plan` · `/transactions` | Billing           | 计费、历史、交易                                      |
| `/apps`                                                             | Apps              | 应用卡片 / 集成                                     |

### 系统页面 + Disabled / Cleanup

| 路由                                                | 功能                 |
| ------------------------------------------------- | ------------------ |
| `/errors/401` · `/403` · `/404` · `/500` · `/503` | 错误页                |
| `/legal`                                          | Terms / Privacy 占位 |
| `/dev/call-detail-test`                           | 开发测试 fixture       |

**Disabled / WIP**: `src/pages/users/index.vue.disabled`(与 `/access-management` 可能重叠)、`src/pages/help-center.vue.disabled`(stub)。**Cleanup 候选**: `activity-timeline-old/` 与 `lead-tracker-old/` 目录仍存在,未启用。

***

## Query Hook 速查(Layer 3b)

> 改 cache strategy / 加新 hook / debug 缓存失效时查。`src/composables/queries/` 共 38 个 hook 文件(2026-05-29 `ls` 实查)。另有多个非 query composable 分散在 `src/composables/` 根,处理 UI 状态(如 `useCalendar`、`useDashboardMetrics`、`useStaffPerformance`),不走 TanStack 缓存。

| Composable                          | 缓存时间      | 用途                               |
| ----------------------------------- | --------- | -------------------------------- |
| `useCalls(storeId, dateRange)`      | 3 分钟      | 通话列表                             |
| `useCallDetail(sessionId)`          | 30 天      | 通话详情(不可变)                        |
| `useRecentCalls()`                  | —         | 最近通话                             |
| `useLinkedCall()`                   | —         | 关联通话                             |
| `useAudioUrl()`                     | —         | 录音预签 URL                         |
| `useLeads(params)`                  | 5 分钟      | 线索列表                             |
| `useConversations(params)`          | **10 分钟** | 对话列表(匹配旧 conversationsCache TTL) |
| `useConversationStatus()`           | 2 分钟      | 对话状态(同文件)                        |
| `useMessagesStore()`                | —         | 消息 store 查询                      |
| `usePhoneNumbers()`                 | 5 分钟      | 电话号码列表                           |
| `usePhoneActivity()`                | —         | 按号码历史                            |
| `useStores()`                       | 5 分钟      | 店铺列表                             |
| `useTypedStores()`                  | —         | 类型化店铺查询                          |
| `useOrganizations()`                | —         | 组织列表(多租户)                        |
| `useConnections()`                  | 5 分钟      | RC 连接状态                          |
| `useOAuth()`                        | —         | OAuth 状态                         |
| `useWebhook()`(即旧 useWebhookStatus) | 30 秒      | Webhook 实时状态                     |
| `useBlackouts()`                    | —         | 禁呼时段                             |
| `useOperatingHours()`               | —         | 营业时间                             |
| `useTasks()`                        | —         | 任务列表                             |
| `useDashboardAnalytics()`           | —         | Dashboard 分析数据                   |
| `useStaffTimelineAnalytics()`       | —         | 员工时间线分析                          |

("—" = 该 hook 未显式设置 staleTime,使用 TanStack Query 默认值 0。)

***

## Pinia Store 速查(Layer 3c)

> 改全局状态 / debug 多 tab 持久化时查。`pinia-plugin-persistedstate` 默认 storage 在 `src/plugins/pinia/index.ts:9` 设为 **`sessionStorage`**(tab 生命周期内持久)。只有 `auth` store 直接用 `localStorage.*` API 手动持久,绕开了 plugin。

| Store                   | 持久化                                             | 关键状态                                                                                                     |
| ----------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| auth                    | **localStorage**(手动 `localStorage.setItem`)     | idToken、accessToken、user、memberships                                                                     |
| stores                  | 否                                               | stores\[]、currentStoreId                                                                                 |
| organization            | sessionStorage(`persist: true`)                 | 当前 org、多租户切换(**核心多租户开关**)                                                                                |
| ringcentral             | **sessionStorage**(手动 `sessionStorage.setItem`) | isConnected、phoneNumbers、OAuth state                                                                     |
| date-range              | 否                                               | startDate、endDate(URL 已是持久载体)                                                                            |
| theme (`system-config`) | sessionStorage(`persist: true`)                 | `radius: number` + `theme: Theme`(8 种色名枚举 `zinc / red / rose / orange / green / blue / yellow / violet`) |
| access-management       | sessionStorage                                  | Access management 页状态                                                                                    |
| analytics-dashboard     | sessionStorage                                  | Dashboard 筛选、tab 状态                                                                                      |
| calls-table             | sessionStorage                                  | Call table 列配置、排序                                                                                        |
| lead-tracker-table      | sessionStorage                                  | Lead tracker 列配置、排序                                                                                      |

***

## 观测(横切)

- **Sentry @sentry/vue**: error tracking;`src/utils/sentry.ts`(env detection + user context);disabled on localhost
- **自建 Session Analytics**: `src/utils/sentry-analytics.ts` 定义 `SessionAnalytics` + `FeatureCategory` enum(DASHBOARD / CALL\_ANALYSIS / FILTERS / EXPORTS …);上报到 Sentry breadcrumb / context
- **不是每个按钮都 log** — 只 log feature usage(用了哪个功能 / 切了哪个 store / 导出了什么)+ error path

> Sentry 接入 / metric 上报细节: 见 [system-design/observability.md](/system-design/observability.md)(待新建,跟踪后续 reorg)

***

## 技术栈速查

| 层级   | 技术                                                                                             |
| ---- | ---------------------------------------------------------------------------------------------- |
| 框架   | Vue 3.5 + TypeScript                                                                           |
| 构建   | Vite 8 (beta)(rolldown-ready;当前仍是 Rollup,未 swap 到 rolldown-vite)                               |
| 样式   | Tailwind CSS v4 + shadcn-vue + Reka UI                                                         |
| 路由   | Vue Router + unplugin-vue-router(文件路由)+ vite-plugin-vue-layouts(blank / default / analysis 布局) |
| 状态   | Pinia + pinia-plugin-persistedstate(默认 `sessionStorage`)                                       |
| 数据获取 | TanStack Query + Axios + Hono RPC(typed API 试点)                                                |
| 图表   | ECharts + vue-echarts + Unovis                                                                 |
| 表单   | Vee-Validate + Zod                                                                             |
| 国际化  | vue-i18n 11.x                                                                                  |
| 认证   | AWS Amplify(Cognito)                                                                           |
| 图标   | Lucide Vue                                                                                     |
| 动画   | motion-v + AutoAnimate                                                                         |
| 工具   | VueUse、Day.js                                                                                  |
| 共享类型 | `@retaintive/common` ^0.54.0(callytics-infrastructure 用 `"*"` = latest)                        |
| 测试   | Vitest + Vue Test Utils + Puppeteer + Lighthouse(perf budget)                                  |
| 代码健康 | Oxlint + Knip(未使用依赖扫描)                                                                         |
| 部署   | Cloudflare Pages(Wrangler),通过 `@sentry/vite-plugin` 在 prod build 上传 sourcemap                  |
| 监控   | Sentry + Google Analytics 4                                                                    |

***

## Legacy

::: info Legacy `retaintive/studio-web` repo
`github.com/retaintive/studio-web` 是 pre-monorepo 版本的前端仓库,已不再活跃(最后 commit 2026-03-04)。所有新工作走 `studio-website-monorepo/apps/web`。如果误落到 `studio-web`,请与团队确认是否待 archive。
:::
