3 changed files with 256 additions and 81 deletions
@ -0,0 +1,66 @@ |
|||
# CLAUDE.md |
|||
|
|||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
|||
|
|||
Popiart SkillHub 前端。技术栈:**React 18 + TypeScript + Vite 5**(非 Vue),UI 用 **Arco Design**(`@arco-design/web-react`)+ **Tailwind CSS v4** + SCSS,状态管理 **Zustand**,路由 **React Router 6**,请求 **Axios**,国际化 **i18next**。 |
|||
|
|||
## 强制规则(改代码/UI/文档前必读) |
|||
|
|||
`AGENTS.md` 将以下 `.cursor` 规则声明为 mandatory: |
|||
- `.cursor/skills/react-best-practices/SKILL.md`(始终生效) |
|||
- `.cursor/rules/changelog-writing.mdc` |
|||
- UI/样式设计参考 `.cursor/skills/frontend-design/SKILL.md`;Figma 像素还原参考 `.cursor/skills/figma-ui-implementation/SKILL.md` |
|||
|
|||
核心硬约束: |
|||
- **禁止 `any`**:ESLint `strictTypeChecked` + `no-explicit-any: error`,lint 零 warning(`--max-warnings 0`)。 |
|||
- **所有 UI 文案必须走 i18n,禁止硬编码。** |
|||
- 只用函数组件 + hooks;state 尽量本地化,少用 `useEffect`/`useMemo`/`useCallback`。 |
|||
- SCSS 嵌套须跟随 DOM 结构、语义化 class、避免深层选择器。 |
|||
- 提交须符合 **Conventional Commits**(commitlint + Husky/lint-staged 提交前校验)。 |
|||
|
|||
## 常用命令 |
|||
|
|||
包管理器 **npm**(仅 `package-lock.json`);无 Node 版本约束(无 `engines`/`.nvmrc`)。 |
|||
|
|||
- `npm run dev` — 开发服务器,端口 **5195**,`--host 0.0.0.0`。 |
|||
- `npm run build` — 默认构建(先 `tsc -p tsconfig.app.json` 类型检查,再 `vite build`)。 |
|||
- `npm run build:stage` / `npm run build:prod` — 预发 / 生产构建(用 `.env.stage` / `.env.prod`)。**注意:这两个不含 `tsc` 类型检查**,要查类型请单独跑 `npm run build` 或 `tsc`。 |
|||
- `npm run preview` — 预览构建产物。 |
|||
- `npm run lint` / `npm run lint:fix` — ESLint。 |
|||
- `npm run format` / `npm run format:check` — Prettier(`printWidth 280`、双引号、分号、`trailingComma all`)。 |
|||
|
|||
**无测试**:项目未配置任何测试框架/脚本。 |
|||
|
|||
## 架构大局(需读多文件才理解的部分) |
|||
|
|||
**入口**:`src/main.tsx` 挂载 `RouterProvider` + `AliveScope`(KeepAlive)+ i18n,并 bootstrap 拉取 paramConfig / memberLevel。`src/App.tsx` 只做全局副作用(埋点、拖拽拦截等),**路由不在 App.tsx 渲染,而在 main.tsx 的 RouterProvider**。 |
|||
|
|||
**路由**(`src/router/index.tsx`):`createBrowserRouter` + 全量 `lazy()` 懒加载。路径统一走 `ROUTES` 常量(`src/constants/index.ts`)。布局分层:多数路由包在 `src/router/LayoutRoot.tsx` 下共享 `Layout`,首页 `/index` 独立于 Layout。**KeepAlive** 用 `react-activation`(`CachedLayoutOutlet.tsx` / `routeCache.ts`)。**权限守卫非 Vue 式 beforeEach**,而是 `src/router/routeGuard.ts`(集中的受保护路由表)+ `LayoutRoot` 内 `LayoutRouteGate`(`useEffect` 拦截,未登录弹 `openLoginDialog`)。 |
|||
|
|||
**状态**(`src/store/`):Zustand,每个 store 一个文件(`useXxxStore.ts`),**无中央 rootStore**。`useUserStore` 用 `persist` 中间件存 localStorage(key `user_info`)。bootstrap 类 store 暴露幂等 `ensureXxx()` 方法。store 内部直接调 `@/api/api` 拉数据。 |
|||
|
|||
**网络层**(`src/api/`): |
|||
- `request.ts` axios 实例,`baseURL: "/api_client/"` —— **固定相对路径,靠 Vite dev proxy 转发**,不直接读 `VITE_API_URL`。 |
|||
- 请求拦截注入 `Authorization: Bearer <localStorage.token>`。 |
|||
- 响应拦截按业务码约定:`status === "0000"` 为成功;`4001` 未登录→清 token 弹登录;`4005` 积分不足→弹充值;`4004` 支付轮询"处理中"放行;其他→`Message.error`。可传 `skipGlobalErrorHandler` 跳过全局处理。 |
|||
- **所有接口集中在单文件 `src/api/api.ts`**(~127 个导出函数 + 类型),并非按模块拆目录。 |
|||
|
|||
**命令式弹窗**(`src/dialog/`):`src/dialog/index.tsx` 是 barrel,导出 `openLoginDialog`、`openInsufficientPointsDialog` 等命令式 API(含 history 栈管理)。登录等交互走这些函数而非页面组件。 |
|||
|
|||
**i18n**(`src/i18n/index.ts`):支持 `zh-CN`/`zh-TW`/`en-US`,默认 `zh-CN`,`keySeparator: false`(用完整字符串作 key)。文案在 `src/locales/*.json`。 |
|||
|
|||
**目录约定**:`src/pages`(按路由,页面目录内 `index.tsx` + `index.scss`)、`src/components`(PascalCase 目录,`index.tsx` + `index.scss` 成对)、`src/layouts/Layout/Layout.tsx`(主框架,含侧栏/顶栏)、`src/hooks`、`src/utils`、`src/constants`。别名 `@` → `src`。`hooks`/`utils`/`dialog`/`constants` 均有 barrel `index` 统一 re-export。 |
|||
|
|||
## 环境与代理(易踩坑) |
|||
|
|||
- Vite dev proxy(`vite.config.mts`):`/api_client`、`/media`、`/resource` → `http://192.168.77.111:8080/`(`devApiTarget`;注释中留有备用 target `wwwtest.popi.art` / `www.popi.art`)。改后端联调地址在这里改。 |
|||
- 三套 `.env`:`.env`(默认/dev)、`.env.stage`、`.env.prod`,`VITE_` 前缀注入。`VITE_API_URL` / `VITE_SKILLHUB_URL` / `VITE_CANVAS_URL` 主要用于**跳转链接**,而非 axios baseURL。 |
|||
- prod 构建:esbuild `drop console/debugger`、无 sourcemap;Rollup `manualChunks` 手动分包(react/arco/swiper/markdown/data vendor)。 |
|||
|
|||
## 响应式适配现状 |
|||
|
|||
本项目 README 定位为 **PC 端(≥1280px)网站**,移动端基本未做真正重排。当前"适配"策略是**等比缩放优先**而非重排: |
|||
- `src/hooks/useViewportScale.ts` 以 **1920px 设计稿**为基准,用 CSS `zoom`/`transform` 缩放整个 Layout 内容区(缩放下限 0.65;宽度 <900px 时关闭缩放,交由页面自身响应式)。 |
|||
- 媒体查询零散存在(约 40 个 scss 文件),但断点**极不统一**(出现过 1778/1680/1500/1280/960/900/768/480 等各种值),**无统一断点体系、无 rem 方案、无 amfe-flexible / postcss-px-to-viewport**。 |
|||
|
|||
做移动端响应式时需正视上述现状(缩放机制与页面自身响应式的边界在 900px)。 |
|||
@ -0,0 +1,168 @@ |
|||
# 响应式适配计划(Popiart SkillHub 前端) |
|||
|
|||
> 本文档为移动端响应式适配的执行计划,按阶段推进。分支:`responsive`(切自 `version/1.2.14`)。 |
|||
|
|||
## Context(背景与目标) |
|||
|
|||
本项目 README 定位为 **PC 端(≥1280px)网站**,移动端基本未做真正重排。当前"适配"靠 `src/hooks/useViewportScale.ts` 以 1920 设计稿等比缩放,窄屏体验差、弹窗溢出、侧栏挤占。现需**让整站在移动端可用**。 |
|||
|
|||
**硬约束:只做 UI 适配,不修改任何业务逻辑 / 数据流 / 接口。** |
|||
|
|||
**三项决策:** |
|||
1. 移动端导航 = **底部 Tab 栏,纯 CSS 实现**(侧栏在窄屏 reflow 到底部,不做抽屉、不加开合 state)。 |
|||
2. 断点范围 = **手机 + 平板**(多档断点)。 |
|||
3. 改动边界 = **允许最小"启用型"改动**:修 viewport meta、新增 `useIsMobile`(matchMedia 封装)。不碰任何业务逻辑。 |
|||
|
|||
**三个关键前提(决定方案骨架):** |
|||
- **viewport 被锁死**:`index.html:17` 的 `<meta name="viewport" content="width=1280, user-scalable=no">` 覆盖了第 7 行的 `width=device-width`,真机被强制按 1280 宽渲染。**不修此行,一切响应式在真机失效** —— 第 0 步必做。 |
|||
- **900px 是天然主断点**:`useViewportScale.ts:17` 的 `LAYOUT_SCALE_DISABLE_BELOW = 900`,窗口 <900px 时自动关闭等比缩放交给媒体查询。**所有"重排型"断点必须 ≤900px**,否则与缩放坐标系打架。≥900px(含平板横屏 1024)仍在缩放系统内,只做轻量微调,不做重排。 |
|||
- **样式几乎全在 SCSS,Tailwind 基本空转**:2724 处 className 绝大多数是 SCSS 语义化 BEM 类;Tailwind 响应式变体 `sm:/md:/lg:` 用了 **0 处**,原子类仅 ~19 处。**Tailwind 的断点/栅格只在 className 生效,无法作用于 `.scss` 里的 `@media`**,而本次要改的 77 处 `@media` 全在 SCSS。→ 结论:主战场是手写 SCSS `@media`,不重写 JSX 为原子类,也不自建平行 token 系统。 |
|||
- **Arco 用得极少(仅 7 个组件)**:Message(35)/Dropdown(14)/Modal(8)/Input(6)/Tooltip(6)/Button(3)/Spin(2)。**未用栅格 Row/Col、未用 Table、未用 Form**——布局 100% 原生 SCSS,Arco 帮不上布局响应式。唯一痛点是 **Modal 固定宽度**,且散在约 9 处(见阶段 2)。无 `ConfigProvider`。 |
|||
|
|||
--- |
|||
|
|||
## 断点体系(对齐 Tailwind 默认,不自建平行系统) |
|||
|
|||
数值**直接沿用 Tailwind v4 默认口径**,仅保留一个自定义主断点 900(与缩放开关硬耦合): |
|||
|
|||
| 名称 | 范围 | 对应 Tailwind | 处理策略 | |
|||
|---|---|---|---| |
|||
| desktop | ≥1024px | ≥lg | 现状 + 等比缩放,基本不动 | |
|||
| tablet-landscape | 900–1024px | lg 区间 | 仍在缩放系统内,仅轻量微调,**不重排** | |
|||
| **mobile-main(主断点)** | **≤900px** | 自定义 | 缩放已自动关闭,**重排主战场**:侧栏→底部 Tab、多列→单/双列、弹窗→全屏 | |
|||
| tablet-portrait | ≤768px | md | 进一步收敛单列、隐藏次要信息 | |
|||
| small-phone | ≤640px | sm | 最小尺寸微调:字号、间距、图标化、按钮全宽 | |
|||
|
|||
> 统一用 `max-width` 媒体查询(与现有方向一致)。**现有 77 处 `@media` 不重构**;新增的直接写 `@media (max-width: 900/768/640px)`,数值口径与 Tailwind 一致,将来若在新 JSX 用 `md:/lg:` 亦不冲突。是否加 SCSS `@mixin respond-to` 属可选糖,非必需。 |
|||
|
|||
--- |
|||
|
|||
## 阶段 0 · 全局基建(先做,是后续所有改造的抓手) |
|||
|
|||
> 基建刻意做薄:**不引入平行 token 系统、不重构现有 @media、不把 JSX 改写成 Tailwind 原子类**。 |
|||
|
|||
| # | 事项 | 文件/位置 | 说明 | |
|||
|---|---|---|---| |
|||
| 0.1 | **修 viewport** | `index.html:17` | 删除/改为 `width=device-width, initial-scale=1`(保留第 7 行即可)。这是一切前提 | |
|||
| 0.2 | **约定断点数值** | 无需新文件;沿用 Tailwind 默认口径 | 统一用 900(主)/768(md)/640(sm)/1024(lg)。新增 @media 直接写数值;如需 SCSS mixin 仅加一个轻量 `@mixin respond-to`(可选)。若个别新 JSX 要响应式类,直接用 Tailwind 自带 `md:/lg:`,不自建 | |
|||
| 0.3 | **`useIsMobile` hook** | 新增 `src/hooks/useIsMobile.ts` + 挂 `src/hooks/index.ts` barrel | `matchMedia("(max-width: 900px)")` 封装,与 `useViewportScale` 的 900 对齐;仅供少数"隐藏 PC 专属 UI"的条件渲染(不改业务逻辑)。底部 Tab 为纯 CSS,不依赖它 | |
|||
| 0.4 | **全局兜底** | `src/index.css` | 全局 `*{min-width:0}` 防 flex/grid 溢出、`img/video{max-width:100%}`、`overflow-x:hidden` 兜底、移动端 `body` 字号基准 | |
|||
|
|||
--- |
|||
|
|||
## 阶段 1 · 应用外壳 Layout → 底部 Tab 栏(纯 CSS) |
|||
|
|||
核心文件:`src/layouts/Layout/Layout.tsx`、`src/layouts/Layout/Layout.scss` |
|||
|
|||
**改造锚点(均在 `Layout.scss`):** |
|||
- `--layout-sidebar-width: 90px`(`:50-51`)、`grid-template-columns`(`:61`)、`grid-template-rows`、`.layoutShell__sidebar`(`:840`)、`.layoutShell__topbar`(`:159`)、`.layoutShell__contentFrame`(`:1069`)。 |
|||
|
|||
**≤900px 的 CSS 重排(不动 TSX 结构、不加 state):** |
|||
1. `.layoutShell` 网格从"左栏 + 右内容"改为**纵向三行**:顶栏 / 内容区(1fr) / 底部导航。`grid-template-columns: 1fr`,`grid-template-rows: auto 1fr auto`。 |
|||
2. `.layoutShell__sidebar`:`grid-row` 改为最后一行、`grid-column:1`;`flex-direction: row`、横向排布、`position: sticky/fixed; bottom:0`、高度约 56–64px、`overflow-x:auto`(导航项多时横滑)、加 `env(safe-area-inset-bottom)` 安全区。品牌 logo(`__brand`) 在窄屏隐藏。主导航 `__sideNav` 与底部 `__footer`(subscribe/profile)合并为一条底部 Tab(复用现有 `LAYOUT_SIDE_NAV_ITEMS`(`Layout.tsx:93`) / `LAYOUT_FOOTER_NAV_ITEMS`(`Layout.tsx:286`) 渲染出的 DOM,纯 CSS 重排,不改数据)。 |
|||
3. `.layoutShell__topbar`:精简——保留登录/头像/积分,隐藏次要项(教程、客服二维码可收进头像菜单,纯 CSS 隐藏);`grid-column:1`。 |
|||
4. `.layoutShell__contentFrame` / `__contentScroll`:单列,底部留出 Tab 栏高度的 `padding-bottom`。 |
|||
5. **与缩放系统协调**:≤900px `useViewportScale` 已返回 1(不缩放),故 `.layoutShellScaleInner`(`Layout.scss:32-45`)在窄屏 `zoom/transform` 自动为 1,无需改 hook;仅需确认 spacer 占位在 scale=1 时不产生多余留白(`Layout.scss` 内 `layoutShellScaleSpacer` 窄屏归零)。 |
|||
|
|||
--- |
|||
|
|||
## 阶段 2 · 弹窗统一改造(抓手:`src/dialog/index.tsx`) |
|||
|
|||
**主注入点**:`openDialogWithHistory()`(`src/dialog/index.tsx` ~L96-190)内 barrel 弹窗汇聚到同一个 `Modal.confirm({...config})`。**在此对 `config.style` 做窄屏降级可一处覆盖多数 barrel 弹窗**:≤900px 统一 `width:100%`、`maxWidth:100vw`、近全屏(复用已有 `layoutFullscreenDetailModalShellStyle`(`:57-67`)作窄屏 shell)。 |
|||
|
|||
**⚠️ Modal 非单点收口,另有约 9 个散点需单独覆盖:** |
|||
- 直接调 `Modal.confirm` 的(3 文件):`src/dialog/pay-subscribe/index.tsx`、`src/pages/Create/panels/FolderPanel/index.tsx`、`src/pages/Profile/index.tsx`。 |
|||
- 直接写 `<Modal>` JSX 的(5 文件):`src/dialog/LoginRegisterModal/index.tsx`、`src/dialog/asset-details/index.tsx` 及其子 `AssetExpandImageModal`/`AssetDetailRepairModal`、`src/components/DeleteConfirmModal/index.tsx`。 |
|||
- 每处按同一窄屏 shell 规则给 `style`/`className`。可考虑抽一个共享的窄屏 modal style 常量(放 `src/dialog/index.tsx` 导出)复用,避免重复。 |
|||
|
|||
**不走 barrel 的自定义 overlay:** `src/dialog/alice-live-class/`(`--dialog-layout-scale` 缩放)、`src/dialog/app-download-mac/`。 |
|||
|
|||
**弹窗逐个(≤900px 目标 = 全屏/单列,内部并排→堆叠):** |
|||
|
|||
| 弹窗目录 | 现状宽度 | 窄屏风险 | 适配要点 | |
|||
|---|---|---|---| |
|||
| `character-picker` | **1699px/80vw** | 极高 | 重灾区:固定 1699 画布 → 全屏、卡片网格降为 2/1 列 | |
|||
| `material-picker` | **1699px/80vw** | 极高 | 同上,全屏 + 单列 | |
|||
| `voice-audio-upload` | 1042px/92vw | 高 | 全屏,波形/表单纵向堆叠 | |
|||
| `points-detail-modal` | fit/100vw-32 | 高 | 大表格:改卡片式列表或横向滚动容器 | |
|||
| `pay-subscribe` | fit/100vw-32 | 高 | 套餐并排→纵向堆叠(配合组件 `subscribe-plan-list`)| |
|||
| `character-detail` | 全屏 shell | 中(内容级) | 宽度 OK,内部"预览+信息"双栏→单列纵向 | |
|||
| `asset-details` | 全屏 shell | 中(内容级) | 同上;子弹窗 `AssetExpandImageModal`/`AssetDetailRepairModal` 已有 @media,补齐 | |
|||
| `folders-asset-details` | 全屏 shell | 中 | 内部双栏→单列 | |
|||
| `LoginRegisterModal` | fit/100vw-32 | 中 | 已有 @media540,补到 480;表单全宽 | |
|||
| `points-recharge` | fit/100vw-32 | 中 | 已有 @media720/480,校验网格 `repeat(4,87px)` | |
|||
| `user-edit` | fit/100vw-32 | 中 | 表单单列全宽 | |
|||
| `gateway-apikey` | fit/100vw-32 | 中 | 已有 @media768,补齐 | |
|||
| `copyright-deposit` | 885px | 中 | 全屏/单列 | |
|||
| `voice-library-picker` | 833px | 中 | 全屏;依赖组件 `VoiceLibraryPicker`(见阶段 4)| |
|||
| `contact-service` | 454px/80vw | 低 | 微调 | |
|||
| `insufficient-points` | fit | 低 | 微调 | |
|||
| `alice-live-class` | 自定义缩放 | 中 | 单独:窄屏改真实全屏布局,脱离 `--dialog-layout-scale` | |
|||
| `app-download-mac` | overlay | 低 | 单独:已有 @media720,补齐 | |
|||
|
|||
> 清理:`src/dialog/Untitled`(21 字节杂散文件)随手删除。 |
|||
|
|||
--- |
|||
|
|||
## 阶段 3 · 页面逐个适配(≤900px 为主,≤768/≤480 收敛) |
|||
|
|||
统一原则:多列网格降列(→2→1)、并排双栏→纵向、定宽 `min-width/固定px`→`min-width:0`+流式、`overflow-x` 区域改可控横滑或换行、按钮/输入窄屏全宽。**均只改 `.scss` 与必要 `className`,不动组件逻辑。** |
|||
|
|||
**优先级 A — 无/弱响应式,窄屏高风险(重点投入)** |
|||
|
|||
| 页面 | 文件 | 适配要点 | |
|||
|---|---|---| |
|||
| **CharactersCreate** | `src/pages/CharactersCreate/index.{tsx,scss}`(最大 scss 1833 行)+ 子 `CreateRoleModal` | 画布型编辑器,多处 `min-width:720/660/494/430`。窄屏:画布区改纵向流、工具/结果面板堆叠、去定宽。**工作量最大** | |
|||
| **Voice** | `src/pages/Voice/index.scss`(4055 行)+ `components/music/*`、`components/speech/VoiceSpeechPanel.tsx`(3226 行)| 左表单/右画廊/播放器/歌词/波形裁剪。窄屏:`repeat(2,…)`→1、`min-width:280/160/125`去定宽、播放器 bar 贴底、裁剪/歌词编辑纵向 | |
|||
| **Profile** | `src/pages/Profile/index.{tsx,scss}` | 仅有 @media900:补 768/480;用户信息头行重排、画廊网格降列、批量选择栏适配 | |
|||
| **Create/InspirationLibraryPanel** | `panels/InspirationLibraryPanel/*`、`BackgroundAssets/*` | 无断点:新增网格降列 | |
|||
|
|||
**优先级 B — 已有较完整响应式(补齐 + 校验)** |
|||
|
|||
| 页面 | 文件 | 适配要点 | |
|||
|---|---|---| |
|||
| index(首页)| `src/pages/index/index.scss`(1709 行)| 营销页多处三栏定宽栅格(`150px 1fr 150px` 等);已有 1320/1100/860/640,补 480、去定宽段 | |
|||
| Explore(+子 feed)| `src/pages/Explore/*` | 响应式最完整(6→1 列);仅校验 Hero `minmax(420px,633px)` 定宽段 ≤480 | |
|||
| Teaching | `src/pages/Teaching/index.scss` | 已 5→1 列;校验 hero 双栏与 `minmax(160px,192fr)` | |
|||
| TeachingDocument | `src/pages/TeachingDocument/*` | 402px 定宽侧栏已有单列降级;校验富文本多列 `--rich-column-count` | |
|||
| Characters | `src/pages/Characters/*` | 卡片墙 + 横滑区,已有 1280/900/560;补 480 | |
|||
| Create 容器 + 其余面板 | `src/pages/Create/*`(`MyCreationsPanel`/`FolderPanel`/`MyFavoritePanel`/`TemplateAssets`/`CharacterAssetsPanel`)| 多含 `overflow-x` 与 `repeat(auto-fill,168px)` 定宽单元格;FolderPanel(1687 行 scss)为重点,补断点 | |
|||
| subscribe | `src/pages/subscribe/*` + Swiper | 已 820 降单列;校验 `repeat(4,87px)` 积分网格、Swiper 窄屏 slidesPerView | |
|||
| content/page | `src/pages/content/page/*` | 已 @media768,基本 OK | |
|||
| NotFound | `src/pages/NotFound/index.tsx` | 纯 Tailwind,天然自适应,无需改 | |
|||
|
|||
--- |
|||
|
|||
## 阶段 4 · 公共组件适配(`src/components/`) |
|||
|
|||
| 组件 | 适配要点 | |
|||
|---|---| |
|||
| `subscribe-plan-list` | 重:并排套餐卡(已 @media820)→ 窄屏纵向堆叠/横滑 | |
|||
| `VoiceLibraryPicker` | 重:列表+试听并排 → 纵向 | |
|||
| `AvatarCropModal` | 重:图片裁剪画布,校验触摸手势与固定尺寸(已 @media768/520)| |
|||
| `UserAvatarMenu` | 下拉菜单定宽(已 @media860),窄屏防越界 | |
|||
| `AssetGalleryTile` / `PreviewImageThumb` | 网格瓦片:流式宽度、`min-width:0` | |
|||
| `AssetAudioPlayer` | 固定宽 → 流式全宽 | |
|||
| `UserGuide` | 新手引导覆盖层:定位随窄屏元素校准 | |
|||
| `CategoryDropdown` / `FolderMoveSubmenu` / `DeleteConfirmModal` | 固定宽 → `max-width:calc(100vw-32px)` | |
|||
| `subscribe-qa-section`(已768)、`TabPill`/`SectionPill`、`HorizontalWheelScroll`、`ErrorBoundary`、`UploadPendingIndicator`、`RouteLoading` | 小改或无需改,逐个校验不溢出 | |
|||
|
|||
--- |
|||
|
|||
## 执行顺序建议 |
|||
|
|||
阶段 0(基建)→ 阶段 1(外壳 Tab 栏,先让整体骨架能用)→ 阶段 2(弹窗统一注入,一处覆盖多数)→ 阶段 3A(重灾页面)→ 阶段 3B(补齐页面)→ 阶段 4(组件)。每阶段独立可提交(Conventional Commits,如 `style(layout): 移动端底部导航栏`)。 |
|||
|
|||
## 验证 |
|||
|
|||
- 本地 `npm run dev`(端口 5195,`--host 0.0.0.0` 可真机同网访问)。 |
|||
- 用 Chrome DevTools 设备模拟逐屏核对:**390px(手机)/ 768px(平板竖)/ 900px(主断点临界)/ 1024px(平板横)/ 1280px(桌面回归)**。 |
|||
- 重点验证清单:① 首屏无横向滚动条(各断点);② 底部 Tab 可点、内容不被遮挡(safe-area);③ 全部弹窗窄屏不溢出、可关闭;④ 重灾区页面(CharactersCreate/Voice)无内容被裁切;⑤ ≥900px 桌面视觉零回归(缩放系统未被破坏)。 |
|||
- 收尾 `npm run lint`(零 warning)+ `npm run build`(tsc 类型检查通过)。 |
|||
- 提示:≤900 与 ≥900 两侧要交叉验证,因为 900 是缩放开关的临界点。 |
|||
|
|||
## 注意事项 / 不做 |
|||
|
|||
- 不改任何 `src/api/*`、`src/store/*` 业务逻辑与数据流。 |
|||
- 不新增业务组件;`useIsMobile` 仅为条件渲染纯 UI(如隐藏/切换展示),非业务分支。 |
|||
- 遵循 `.cursor/skills/react-best-practices`:SCSS 嵌套跟随 DOM、语义化 class;如需新增任何可见文案须走 i18n(本次应尽量零新增文案)。 |
|||
Loading…
Reference in new issue