commit a0929de768a1bf646116e4076014dc5b26e3d15e Author: lanrtop Date: Tue Apr 7 15:23:39 2026 +0900 chore: 初始化项目结构与蓝图 - 配置 pnpm workspace monorepo 基础结构 - 初始化 .blueprint/ 蓝图:6 个业务模块、21 张任务卡 - 目标:Z Fold 6 折叠屏不动产物品摸底统计 App(React Native + Expo) Co-Authored-By: Claude Sonnet 4.6 diff --git a/.blueprint/manifest.yaml b/.blueprint/manifest.yaml new file mode 100644 index 0000000..e11f731 --- /dev/null +++ b/.blueprint/manifest.yaml @@ -0,0 +1,222 @@ +version: 1 +name: react-phone-apps +iteration: 1 +updated: 2026-04-07 + +areas: + - id: infrastructure + name: 基础设施 + color: "#6B7280" + - id: shared + name: 共享层 + color: "#8B5CF6" + - id: data + name: 数据层 + color: "#F59E0B" + - id: rn-app + name: RN 应用(不动产摸底) + color: "#10B981" + - id: frontend + name: 前端应用(Web) + color: "#3B82F6" + +modules: + # ── 基础设施 ────────────────────────────────────────────────── + - id: monorepo-setup + name: Monorepo 工程配置 + area: infrastructure + status: done + progress: 100 + position: [80, 80] + + # ── 共享层 ──────────────────────────────────────────────────── + - id: shared-ui + name: 共享 UI 组件库 + area: shared + status: concept + progress: 0 + position: [380, 80] + + - id: shared-utils + name: 共享工具函数库 + area: shared + status: concept + progress: 0 + position: [380, 240] + + - id: shared-hooks + name: 共享 React Hooks + area: shared + status: concept + progress: 0 + position: [380, 400] + + - id: shared-types + name: 共享类型定义 + area: shared + status: concept + progress: 0 + position: [380, 560] + + # ── 数据层 ──────────────────────────────────────────────────── + - id: local-data-layer + name: 本地数据持久化层 + area: data + status: planned + progress: 0 + position: [680, 80] + + # ── RN 应用(不动产摸底)──────────────────────────────────── + - id: survey-app-shell + name: 应用外壳与折叠屏布局 + area: rn-app + status: planned + progress: 0 + position: [980, 80] + + - id: floor-map + name: 楼层地图与标记点管理 + area: rn-app + status: planned + progress: 0 + position: [980, 280] + + - id: item-form + name: 物品登记表单 + area: rn-app + status: planned + progress: 0 + position: [980, 480] + + - id: photo-capture + name: 照片采集与管理 + area: rn-app + status: planned + progress: 0 + position: [980, 680] + + - id: inventory-summary + name: 汇总列表与统计 + area: rn-app + status: planned + progress: 0 + position: [980, 880] + + # ── 前端应用(Web,预留)────────────────────────────────────── + - id: app-scaffold + name: 应用脚手架模板 + area: frontend + status: concept + progress: 0 + position: [1280, 80] + + - id: routing + name: 路由与页面结构 + area: frontend + status: concept + progress: 0 + position: [1280, 240] + + - id: theming + name: 主题与样式系统 + area: frontend + status: concept + progress: 0 + position: [1280, 400] + + - id: state-management + name: 全局状态管理 + area: frontend + status: concept + progress: 0 + position: [1280, 560] + + - id: api-client + name: API 客户端层 + area: frontend + status: concept + progress: 0 + position: [1280, 720] + +edges: + # 基础设施 → 共享层 + - from: monorepo-setup + to: shared-ui + type: dependency + - from: monorepo-setup + to: shared-utils + type: dependency + - from: monorepo-setup + to: shared-hooks + type: dependency + - from: monorepo-setup + to: shared-types + type: dependency + - from: monorepo-setup + to: local-data-layer + type: dependency + - from: monorepo-setup + to: survey-app-shell + type: dependency + + # 共享类型 → 其他共享层 + - from: shared-types + to: shared-ui + type: dependency + - from: shared-types + to: shared-utils + type: dependency + - from: shared-types + to: api-client + type: dependency + + # 数据层 → RN 应用 + - from: local-data-layer + to: survey-app-shell + type: dependency + - from: local-data-layer + to: floor-map + type: dependency + - from: local-data-layer + to: item-form + type: dependency + - from: local-data-layer + to: photo-capture + type: dependency + - from: local-data-layer + to: inventory-summary + type: dependency + + # RN 应用内部依赖 + - from: survey-app-shell + to: floor-map + type: dependency + - from: survey-app-shell + to: inventory-summary + type: dependency + - from: floor-map + to: item-form + type: dependency + - from: item-form + to: photo-capture + type: dependency + + # Web 前端(预留) + - from: monorepo-setup + to: app-scaffold + type: dependency + - from: shared-ui + to: app-scaffold + type: dependency + - from: app-scaffold + to: routing + type: dependency + - from: app-scaffold + to: theming + type: dependency + - from: app-scaffold + to: state-management + type: dependency + - from: app-scaffold + to: api-client + type: dependency diff --git a/.blueprint/modules/api-client.md b/.blueprint/modules/api-client.md new file mode 100644 index 0000000..034c8be --- /dev/null +++ b/.blueprint/modules/api-client.md @@ -0,0 +1,30 @@ +--- +id: api-client +name: API 客户端层 +area: frontend +status: concept +--- + +## 概述 + +封装后端 API 调用,包含统一鉴权、错误处理和接口类型定义。以 packages/api 形式跨 app 共享,或在各 app 内独立维护。 + +## 任务卡 + +### 💭 HTTP 客户端实例配置 +- status: concept +- complexity: M +- files: packages/utils/src/request.ts, apps/app-one/src/api/client.ts +- acceptance: 配置 baseURL、超时时间,请求拦截器自动附加 Authorization header,响应拦截器统一处理 401/403/500 错误码 + +### 💭 API 接口类型化封装 +- status: concept +- complexity: M +- files: apps/app-one/src/api/types.ts, apps/app-one/src/api/auth.ts +- acceptance: 每个 API 模块导出类型安全的请求函数(入参和返回值均有 TS 类型),不使用 any + +### 💭 数据请求缓存与 SWR 集成 +- status: concept +- complexity: M +- files: apps/app-one/src/api/hooks/useAuthQuery.ts +- acceptance: 基于 SWR 或 React Query 封装常用接口 hook,支持缓存、重试、乐观更新,避免重复请求 diff --git a/.blueprint/modules/app-scaffold.md b/.blueprint/modules/app-scaffold.md new file mode 100644 index 0000000..eeb1f81 --- /dev/null +++ b/.blueprint/modules/app-scaffold.md @@ -0,0 +1,28 @@ +--- +id: app-scaffold +name: 应用脚手架模板 +area: frontend +status: concept +--- + +## 概述 + +新 app 的起始模板,包含 Vite 项目配置、HTML 入口、React 根组件、环境变量规范。monorepo 中每个 app 从此模板派生。 + +## 任务卡 + +### 💭 创建第一个 App(apps/app-one) +- status: concept +- complexity: M +- files: apps/app-one/package.json, apps/app-one/vite.config.ts, apps/app-one/index.html, apps/app-one/src/main.tsx, apps/app-one/src/App.tsx +- acceptance: pnpm dev 能启动,pnpm build 产出 dist/,src/App.tsx 渲染 Hello World,正确引入 @repo/ui 和 @repo/utils + +### 💭 移动端视口基础配置 +- status: concept +- complexity: S +- files: apps/app-one/index.html, apps/app-one/src/styles/global.css +- acceptance: meta viewport 设置 initial-scale=1,禁用双击缩放,全局 CSS reset 适配移动端(100dvh、touch-action 等) + +## 决策记录 + +- 每个 app 独立配置 Vite 而非共享一个构建进程:便于各 app 独立部署和差异化配置,构建缓存也可隔离 diff --git a/.blueprint/modules/floor-map.md b/.blueprint/modules/floor-map.md new file mode 100644 index 0000000..733a2b8 --- /dev/null +++ b/.blueprint/modules/floor-map.md @@ -0,0 +1,40 @@ +--- +id: floor-map +name: 楼层地图与标记点管理 +area: rn-app +status: planned +--- + +## 概述 + +以 SVG/Image 占位图为底图,支持长按添加标记点、点击标记点进入编辑的交互逻辑。标记点坐标以相对比例(0~1)存储,替换真实楼层图时无需修改数据。 + +## 任务卡 + +### 📋 平面图占位容器与坐标系 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/map/FloorMap.tsx +- depends: survey-app-shell +- acceptance: 渲染一个带灰色背景的矩形占位区域作为平面图,使用 onLayout 获取实际像素尺寸并转换相对坐标(0~1),容器宽高比固定为 4:3,适配折叠/展开两种宽度 + +### 📋 长按手势添加标记点 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/map/useMarkerGesture.ts +- depends: floor-map +- acceptance: 在平面图区域长按 500ms 后触发,将触点位置转换为相对坐标并调用 insertMarker 写入数据库,新标记点立即在地图上显示,不误触短按事件 + +### 📋 标记点图标组件 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/map/MapMarker.tsx +- depends: floor-map +- acceptance: 在平面图上按相对坐标绝对定位渲染图钉图标,已有物品的标记点显示蓝色图钉 + 物品数量角标,空标记点显示灰色图钉,点击触发 onPress 回调 + +### 📋 当前楼层标记点数据加载 +- status: todo +- complexity: S +- files: apps/property-survey/src/features/map/MarkerList.tsx +- depends: floor-map +- acceptance: 楼层切换时调用 getMarkersByFloor 重新查询,渲染该楼层所有 MapMarker,删除标记点时弹出确认对话框(含级联删除提示) diff --git a/.blueprint/modules/inventory-summary.md b/.blueprint/modules/inventory-summary.md new file mode 100644 index 0000000..7030217 --- /dev/null +++ b/.blueprint/modules/inventory-summary.md @@ -0,0 +1,40 @@ +--- +id: inventory-summary +name: 汇总列表与统计 +area: rn-app +status: planned +--- + +## 概述 + +汇总展示全部已登记物品,支持按楼层和物品名筛选,底部显示总件数和总金额统计。提供 CSV 导出通过系统分享面板发送备份。 + +## 任务卡 + +### 📋 汇总数据查询 Hook +- status: todo +- complexity: M +- files: apps/property-survey/src/features/summary/useSummaryData.ts +- depends: local-data-layer +- acceptance: 查询所有 items JOIN markers JOIN photos,返回带楼层、坐标、物品名、数量、单价、备注、照片数的扁平列表;支持按 floor(1/2/3/全部)和 name 关键字过滤;数据写入后调用 refresh() 触发重新查询 + +### 📋 汇总列表与筛选栏 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/summary/SummaryList.tsx, apps/property-survey/src/features/summary/FilterBar.tsx +- depends: inventory-summary +- acceptance: 列表每行显示楼层标签、物品名、数量、单价、备注1;FilterBar 提供楼层选择器(1F/2F/3F/全部)和物品名搜索框;滚动性能流畅(FlatList),空数据时显示引导文案 + +### 📋 统计汇总卡片 +- status: todo +- complexity: S +- files: apps/property-survey/src/features/summary/SummaryStats.tsx +- depends: inventory-summary +- acceptance: 固定在列表顶部,展示:登记点总数、物品种类数、总数量合计、总金额合计(数量×单价求和,单价为空则不计入),数值随筛选条件实时更新 + +### 📋 CSV 导出与系统分享 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/summary/exportData.ts +- depends: inventory-summary +- acceptance: 点击导出按钮,将当前筛选结果生成 UTF-8 BOM 的 CSV 文件(含表头:楼层/标记点名/物品名/数量/单价/备注1/备注2/照片数),写入临时目录后调用 expo-sharing 弹出系统分享面板,可发送到微信/邮件/云盘 diff --git a/.blueprint/modules/item-form.md b/.blueprint/modules/item-form.md new file mode 100644 index 0000000..60d9f37 --- /dev/null +++ b/.blueprint/modules/item-form.md @@ -0,0 +1,33 @@ +--- +id: item-form +name: 物品登记表单 +area: rn-app +status: planned +--- + +## 概述 + +点击地图标记点后弹出的物品编辑面板。折叠时以底部抽屉形式展现,展开时在右侧栏内联显示。支持单个标记点下的多条物品记录增删改。 + +## 任务卡 + +### 📋 底部抽屉/右栏容器 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/item/ItemFormSheet.tsx +- depends: survey-app-shell, floor-map +- acceptance: 折叠时从屏幕底部滑出(BottomSheet),展开时在 FoldableLayout 右栏内渲染;接受 markerId 和 onClose 两个 prop;顶部显示标记点位置名称和所属楼层 + +### 📋 物品字段表单 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/item/ItemFields.tsx, apps/property-survey/src/features/item/useItemForm.ts +- depends: item-form +- acceptance: 包含物品名称(必填文本)、数量(数字键盘)、单价(数字键盘,可选)、备注1(多行文本)、备注2(多行文本)五个字段;保存时校验物品名称非空;useItemForm 管理字段状态和 insertItem / updateItem 调用 + +### 📋 标记点下物品列表 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/item/ItemList.tsx +- depends: item-form +- acceptance: 在表单上方列出当前标记点的所有物品(物品名 + 数量 + 单价),点击条目进入编辑,左滑显示删除按钮,点击「+新增物品」清空表单进入新增模式 diff --git a/.blueprint/modules/local-data-layer.md b/.blueprint/modules/local-data-layer.md new file mode 100644 index 0000000..3e81dd3 --- /dev/null +++ b/.blueprint/modules/local-data-layer.md @@ -0,0 +1,39 @@ +--- +id: local-data-layer +name: 本地数据持久化层 +area: data +status: planned +--- + +## 概述 + +基于 Expo SQLite 的本地数据库层,管理标记点、物品记录和照片路径三张核心表。所有写操作使用事务保证原子性,供上层功能模块调用。 + +## 任务卡 + +### 📋 SQLite 初始化与 Schema 建表 +- status: todo +- complexity: M +- files: apps/property-survey/src/db/database.ts, apps/property-survey/src/db/schema.ts +- acceptance: App 启动时自动执行 CREATE TABLE IF NOT EXISTS,建立 markers / items / photos 三张表,重复启动不报错,字段与设计文档一致 + +### 📋 标记点 CRUD 操作 +- status: todo +- complexity: M +- files: apps/property-survey/src/db/markers.ts +- depends: local-data-layer +- acceptance: 提供 insertMarker / getMarkersByFloor / updateMarker / deleteMarker 四个函数,deleteMarker 级联删除关联 items 和 photos,所有函数返回类型安全的 Promise + +### 📋 物品记录 CRUD 操作 +- status: todo +- complexity: M +- files: apps/property-survey/src/db/items.ts +- depends: local-data-layer +- acceptance: 提供 insertItem / getItemsByMarker / updateItem / deleteItem 四个函数,支持批量查询(传入 marker_id 数组),返回带关联照片数量的扩展类型 + +### 📋 照片路径 CRUD 操作 +- status: todo +- complexity: S +- files: apps/property-survey/src/db/photos.ts +- depends: local-data-layer +- acceptance: 提供 insertPhoto / getPhotosByItem / deletePhoto / reorderPhotos 四个函数,deletePhoto 同步删除设备文件系统中的对应图片文件 diff --git a/.blueprint/modules/monorepo-setup.md b/.blueprint/modules/monorepo-setup.md new file mode 100644 index 0000000..ac88845 --- /dev/null +++ b/.blueprint/modules/monorepo-setup.md @@ -0,0 +1,40 @@ +--- +id: monorepo-setup +name: Monorepo 工程配置 +area: infrastructure +status: done +--- + +## 概述 + +pnpm workspace 单体仓库基础配置,涵盖包管理、构建脚本和 Node.js 引擎约束。所有 apps/* 和 packages/* 自动纳入 workspace。 + +## 任务卡 + +### ✅ 初始化 pnpm workspace +- status: done +- complexity: S +- files: package.json, pnpm-workspace.yaml +- acceptance: pnpm-workspace.yaml 正确声明 apps/* 和 packages/*,根 package.json 包含 dev/build/lint 脚本,engines 约束 node>=18 pnpm>=8 + +### 💭 统一 TypeScript 基础配置 +- status: concept +- complexity: S +- files: tsconfig.base.json +- acceptance: 根目录提供 tsconfig.base.json,各子包通过 extends 继承,启用 strict 模式 + +### 💭 统一 ESLint / Prettier 配置 +- status: concept +- complexity: S +- files: eslint.config.js, .prettierrc +- acceptance: 根目录提供共享 lint 规则,各子包可通过 overrides 扩展,pnpm lint 从根目录一键运行 + +### 💭 构建工具链选型 +- status: concept +- complexity: M +- files: packages/build-config/package.json, packages/build-config/vite.base.ts +- acceptance: 封装 Vite 基础配置到 packages/build-config,各 app 通过 extends 引入,减少重复配置 + +## 决策记录 + +- 选择 pnpm workspace 而非 Turborepo/Nx:初期项目规模小,pnpm 原生 workspace 足够,避免引入额外工具链复杂度 diff --git a/.blueprint/modules/photo-capture.md b/.blueprint/modules/photo-capture.md new file mode 100644 index 0000000..62a4b03 --- /dev/null +++ b/.blueprint/modules/photo-capture.md @@ -0,0 +1,26 @@ +--- +id: photo-capture +name: 照片采集与管理 +area: rn-app +status: planned +--- + +## 概述 + +调用设备摄像头或相册为物品条目添加多张照片,照片压缩后存储到 App 专属目录,路径写入 SQLite photos 表。 + +## 任务卡 + +### 📋 摄像头调用与图片压缩 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/photo/usePhotoCapture.ts +- depends: local-data-layer +- acceptance: 使用 expo-image-picker 提供「拍照」和「从相册选择」两种入口;拍摄/选择后自动压缩至最大宽度 1080px、JPEG quality 0.75;将压缩后的文件复制到 expo-file-system 的 App 专属目录;调用 insertPhoto 写入数据库;全流程有权限检查,拒绝权限时给出提示 + +### 📋 照片网格预览与删除 +- status: todo +- complexity: M +- files: apps/property-survey/src/features/photo/PhotoGrid.tsx, apps/property-survey/src/features/photo/PhotoPicker.tsx +- depends: photo-capture +- acceptance: 以 3 列网格展示已添加的照片缩略图;点击照片全屏预览(支持左右滑动切换);长按照片显示「删除」确认弹窗,删除时同步调用 deletePhoto(含文件系统删除);末尾显示「+」按钮触发 PhotoPicker 添加更多 diff --git a/.blueprint/modules/routing.md b/.blueprint/modules/routing.md new file mode 100644 index 0000000..fca8ebe --- /dev/null +++ b/.blueprint/modules/routing.md @@ -0,0 +1,30 @@ +--- +id: routing +name: 路由与页面结构 +area: frontend +status: concept +--- + +## 概述 + +基于 React Router 的单页应用路由体系,含懒加载、受保护路由和移动端页面切换动画。 + +## 任务卡 + +### 💭 路由基础配置 +- status: concept +- complexity: M +- files: apps/app-one/src/router/index.tsx, apps/app-one/src/router/routes.ts +- acceptance: 使用 react-router-dom v6 createBrowserRouter,配置首页和 404 页,支持代码分割(lazy + Suspense) + +### 💭 受保护路由(需登录) +- status: concept +- complexity: M +- files: apps/app-one/src/router/ProtectedRoute.tsx +- acceptance: 未登录访问受保护路由自动跳转登录页,登录后重定向回原目标页,支持角色权限扩展 + +### 💭 移动端页面切换动画 +- status: concept +- complexity: M +- files: apps/app-one/src/router/PageTransition.tsx +- acceptance: 路由切换时有 slide-in/slide-out 动画,与浏览器前进/后退方向一致,不影响首屏性能 diff --git a/.blueprint/modules/shared-hooks.md b/.blueprint/modules/shared-hooks.md new file mode 100644 index 0000000..1415e5c --- /dev/null +++ b/.blueprint/modules/shared-hooks.md @@ -0,0 +1,36 @@ +--- +id: shared-hooks +name: 共享 React Hooks +area: shared +status: concept +--- + +## 概述 + +跨 app 复用的 React 自定义 Hooks,以 packages/hooks 形式发布。依赖 React,不依赖具体 UI 框架。 + +## 任务卡 + +### 💭 搭建 hooks 库骨架 +- status: concept +- complexity: S +- files: packages/hooks/package.json, packages/hooks/src/index.ts +- acceptance: packages/hooks 可被 workspace 内任意 React 应用通过 @repo/hooks 引入,peer dependency 声明 react + +### 💭 数据请求 Hook(useRequest) +- status: concept +- complexity: M +- files: packages/hooks/src/useRequest.ts +- acceptance: 支持 loading/data/error 三态,支持手动触发和依赖自动触发,支持取消请求(AbortController) + +### 💭 移动端交互 Hooks +- status: concept +- complexity: M +- files: packages/hooks/src/useTouch.ts, packages/hooks/src/useScrollLock.ts, packages/hooks/src/useVirtualKeyboard.ts +- acceptance: useTouch 处理 swipe 手势,useScrollLock 控制 body 滚动,useVirtualKeyboard 检测键盘弹出并调整视口 + +### 💭 持久化状态 Hook(useLocalStorage) +- status: concept +- complexity: S +- files: packages/hooks/src/useLocalStorage.ts +- acceptance: 封装 useState + localStorage 同步,支持 SSR 安全(无 window 时优雅降级),TypeScript 泛型 diff --git a/.blueprint/modules/shared-types.md b/.blueprint/modules/shared-types.md new file mode 100644 index 0000000..1bdbcd6 --- /dev/null +++ b/.blueprint/modules/shared-types.md @@ -0,0 +1,30 @@ +--- +id: shared-types +name: 共享类型定义 +area: shared +status: concept +--- + +## 概述 + +跨 app 和 packages 共享的 TypeScript 类型定义,以 packages/types 形式发布。纯类型文件,无运行时代码。 + +## 任务卡 + +### 💭 搭建类型包骨架 +- status: concept +- complexity: S +- files: packages/types/package.json, packages/types/src/index.ts +- acceptance: packages/types 仅包含 .d.ts 或 type-only TS 文件,被引入时不产生任何运行时代码 + +### 💭 通用业务类型 +- status: concept +- complexity: S +- files: packages/types/src/common.ts +- acceptance: 定义 ApiResponse、PaginatedResult、Nullable 等通用泛型类型,供所有 app 和 packages 使用 + +### 💭 环境与配置类型 +- status: concept +- complexity: S +- files: packages/types/src/env.d.ts +- acceptance: 声明 import.meta.env 类型扩展,各 app 的环境变量在 IDE 中有类型提示 diff --git a/.blueprint/modules/shared-ui.md b/.blueprint/modules/shared-ui.md new file mode 100644 index 0000000..2107648 --- /dev/null +++ b/.blueprint/modules/shared-ui.md @@ -0,0 +1,36 @@ +--- +id: shared-ui +name: 共享 UI 组件库 +area: shared +status: concept +--- + +## 概述 + +跨 app 复用的 React UI 组件集合,以 packages/ui 形式发布到 workspace 内部。面向移动端适配(触摸友好、响应式)。 + +## 任务卡 + +### 💭 搭建组件库骨架 +- status: concept +- complexity: M +- files: packages/ui/package.json, packages/ui/src/index.ts, packages/ui/vite.config.ts +- acceptance: packages/ui 可被 apps/* 通过 workspace:* 引入,支持 tree-shaking,导出类型声明文件 + +### 💭 基础原子组件 +- status: concept +- complexity: M +- files: packages/ui/src/components/Button.tsx, packages/ui/src/components/Input.tsx, packages/ui/src/components/Card.tsx +- acceptance: Button/Input/Card 组件覆盖常用变体,props 类型完整,支持 className 扩展 + +### 💭 移动端导航组件 +- status: concept +- complexity: M +- files: packages/ui/src/components/BottomNav.tsx, packages/ui/src/components/TopBar.tsx +- acceptance: BottomNav 支持图标+文字标签,TopBar 支持返回按钮和标题插槽,两者均适配 safe-area-inset + +### 💭 反馈类组件(Toast / Modal / Loading) +- status: concept +- complexity: M +- files: packages/ui/src/components/Toast.tsx, packages/ui/src/components/Modal.tsx, packages/ui/src/components/Loading.tsx +- acceptance: Toast 支持 success/error/info 三种类型,Modal 支持受控和非受控,Loading 可全屏覆盖 diff --git a/.blueprint/modules/shared-utils.md b/.blueprint/modules/shared-utils.md new file mode 100644 index 0000000..7a8a1a6 --- /dev/null +++ b/.blueprint/modules/shared-utils.md @@ -0,0 +1,36 @@ +--- +id: shared-utils +name: 共享工具函数库 +area: shared +status: concept +--- + +## 概述 + +跨 app 复用的纯函数工具集,以 packages/utils 形式发布到 workspace 内部。不依赖 React,可在任何环境使用。 + +## 任务卡 + +### 💭 搭建工具库骨架 +- status: concept +- complexity: S +- files: packages/utils/package.json, packages/utils/src/index.ts +- acceptance: packages/utils 可被 workspace 内任意包通过 @repo/utils 引入,导出类型声明文件 + +### 💭 格式化工具集 +- status: concept +- complexity: S +- files: packages/utils/src/format.ts +- acceptance: 提供日期格式化、数字格式化(货币/百分比)、手机号脱敏等常用函数,每个函数有单元测试 + +### 💭 本地存储工具 +- status: concept +- complexity: S +- files: packages/utils/src/storage.ts +- acceptance: 封装 localStorage/sessionStorage,支持 JSON 序列化和过期时间,TypeScript 泛型推导 + +### 💭 HTTP 请求工具 +- status: concept +- complexity: M +- files: packages/utils/src/request.ts +- acceptance: 基于 fetch 封装,支持请求/响应拦截器、统一错误处理、超时控制,返回类型安全的 Promise diff --git a/.blueprint/modules/state-management.md b/.blueprint/modules/state-management.md new file mode 100644 index 0000000..f300ff3 --- /dev/null +++ b/.blueprint/modules/state-management.md @@ -0,0 +1,28 @@ +--- +id: state-management +name: 全局状态管理 +area: frontend +status: concept +--- + +## 概述 + +跨组件的全局状态方案,覆盖用户会话、UI 状态等场景。轻量优先(Zustand),避免过度设计。 + +## 任务卡 + +### 💭 用户会话 Store +- status: concept +- complexity: M +- files: apps/app-one/src/stores/authStore.ts +- acceptance: 使用 Zustand 管理 user/token/isLoggedIn,提供 login/logout action,token 持久化到 localStorage + +### 💭 全局 UI 状态 Store +- status: concept +- complexity: S +- files: apps/app-one/src/stores/uiStore.ts +- acceptance: 管理全局 loading、toast 消息队列、modal 开关状态,与 packages/ui 的反馈组件联动 + +## 决策记录 + +- 选择 Zustand 而非 Redux:移动端场景状态相对简单,Zustand API 更轻量,无需 boilerplate,TypeScript 支持良好 diff --git a/.blueprint/modules/survey-app-shell.md b/.blueprint/modules/survey-app-shell.md new file mode 100644 index 0000000..4542430 --- /dev/null +++ b/.blueprint/modules/survey-app-shell.md @@ -0,0 +1,39 @@ +--- +id: survey-app-shell +name: 应用外壳与折叠屏布局 +area: rn-app +status: planned +--- + +## 概述 + +React Native (Expo SDK 52 + Expo Router) 应用骨架,实现 Z Fold 6 折叠/展开双形态布局切换。折叠时单栏显示,展开时左侧地图 + 右侧表单双栏并排。 + +## 任务卡 + +### 📋 Expo 应用骨架初始化 +- status: todo +- complexity: M +- files: apps/property-survey/app.json, apps/property-survey/package.json, apps/property-survey/app/_layout.tsx, apps/property-survey/app/index.tsx, apps/property-survey/app/summary.tsx +- depends: local-data-layer +- acceptance: `pnpm --filter property-survey start` 能在 Expo Go 或开发构建中启动,底部导航可在「登记」和「汇总」两个页面间切换,SQLite 数据库在启动时完成初始化 + +### 📋 折叠屏状态感知 Hook +- status: todo +- complexity: S +- files: apps/property-survey/src/layout/useFoldState.ts +- acceptance: useFoldState 返回 `{ isUnfolded: boolean, windowWidth: number }`,以 720px 为断点区分折叠/展开状态,useWindowDimensions 变化时自动更新,在模拟器宽度调整时可观察到状态切换 + +### 📋 双栏/单栏自适应布局容器 +- status: todo +- complexity: M +- files: apps/property-survey/src/layout/FoldableLayout.tsx +- depends: survey-app-shell +- acceptance: 展开时渲染左右两栏(左侧 flexGrow:1 地图区,右侧 380px 详情区),折叠时只渲染左侧(地图全屏),接受 `left` 和 `right` 两个 ReactNode 插槽 + +### 📋 楼层切换标签组件 +- status: todo +- complexity: S +- files: apps/property-survey/src/layout/FloorTabs.tsx +- depends: survey-app-shell +- acceptance: 显示 1F / 2F / 3F 三个标签,选中项高亮,onChange 回调传出当前楼层数字(1/2/3),组件宽度自适应容器 diff --git a/.blueprint/modules/theming.md b/.blueprint/modules/theming.md new file mode 100644 index 0000000..66dfc1c --- /dev/null +++ b/.blueprint/modules/theming.md @@ -0,0 +1,30 @@ +--- +id: theming +name: 主题与样式系统 +area: frontend +status: concept +--- + +## 概述 + +基于 CSS 变量的主题系统,支持亮色/暗色模式切换,结合 Tailwind CSS 或 CSS Modules 实现统一的视觉风格。 + +## 任务卡 + +### 💭 设计 Token 与 CSS 变量 +- status: concept +- complexity: S +- files: packages/ui/src/styles/tokens.css +- acceptance: 定义颜色、间距、字体、圆角等设计 token 为 CSS 变量,dark 模式通过 prefers-color-scheme 或 [data-theme] 切换 + +### 💭 主题切换逻辑 +- status: concept +- complexity: S +- files: apps/app-one/src/hooks/useTheme.ts +- acceptance: 提供 useTheme hook,支持手动切换和跟随系统,偏好持久化到 localStorage + +### 💭 Tailwind 配置集成 +- status: concept +- complexity: S +- files: packages/build-config/tailwind.base.ts, apps/app-one/tailwind.config.ts +- acceptance: 共享 Tailwind 配置,设计 token 与 Tailwind 主题颜色/间距对齐,各 app 可扩展 diff --git a/.blueprint/usage.json b/.blueprint/usage.json new file mode 100644 index 0000000..0063a52 --- /dev/null +++ b/.blueprint/usage.json @@ -0,0 +1,28 @@ +{ + "project_name": "react-phone-apps", + "snapshots": [ + { + "at": "2026-04-07T06:17:15Z", + "blocked_reasons": [], + "conventions_version": "unknown", + "iteration": 1, + "modules": { + "blocked": 0, + "concept": 9, + "done": 1, + "in_progress": 0, + "planned": 0, + "stalled": [], + "total": 10 + }, + "tasks": { + "blocked": 0, + "dispatched": 0, + "done": 1, + "in_progress": 0, + "todo": 0, + "total": 32 + } + } + ] +} \ No newline at end of file diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..9f80699 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "lian-jing:group-da6dc719": { + "type": "http", + "url": "http://localhost:27190/mcp/group/da6dc719" + } + } +} \ No newline at end of file diff --git a/.codemap/main-panel.json b/.codemap/main-panel.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/.codemap/main-panel.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e8542a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +.next/ +*.log +.env +.env.* +!.env.example + +# Windows 保留设备名(误创建的空文件) +/nul diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..19be10e --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +strict-peer-dependencies=false +auto-install-peers=true diff --git a/apps/.gitkeep b/apps/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/fix-scoop-git.ps1 b/fix-scoop-git.ps1 new file mode 100644 index 0000000..8eea94f --- /dev/null +++ b/fix-scoop-git.ps1 @@ -0,0 +1,134 @@ +Write-Host "修复 Scoop 和 Git 安装..." + +$scoopDir = "E:\Scoop" +$appsDir = "$scoopDir\apps" +$shimsDir = "$scoopDir\shims" + +# 1. 修复 7zip shim +Write-Host "1. 修复 7zip shim..." +$7zShim = "$shimsDir\7z.shim" +$7zPath = "$appsDir\7zip\25.01\7z.exe" +if (Test-Path $7zPath) { + if (Test-Path $7zShim) { + Set-Content $7zShim "path = `"$7zPath`"" + Write-Host " 更新 7z.shim 指向: $7zPath" + } else { + Write-Host " 错误: 7z.shim 不存在" + } +} else { + Write-Host " 错误: 7z.exe 未找到,请先安装 7-zip: scoop install 7zip" + exit 1 +} + +# 2. 修复 7zip current 符号链接 +Write-Host "2. 修复 7zip current 符号链接..." +$7zipCurrent = "$appsDir\7zip\current" +if (-not (Test-Path $7zipCurrent)) { + cmd /c mklink /J "$7zipCurrent" "$appsDir\7zip\25.01" 2>$null + if (Test-Path $7zipCurrent) { + Write-Host " 创建 current 符号链接" + } else { + Write-Host " 警告: 无法创建符号链接,使用绝对路径" + } +} else { + Write-Host " current 符号链接已存在" +} + +# 3. 手动提取 Git +Write-Host "3. 手动提取 Git..." +$gitDir = "$appsDir\git\2.53.0.2" +$dl7z = "$gitDir\dl.7z" +if (Test-Path $dl7z) { + Write-Host " 找到 dl.7z: $dl7z" + + # 创建目标目录 + if (-not (Test-Path $gitDir)) { + New-Item -ItemType Directory -Path $gitDir -Force | Out-Null + } + + # 使用 7z 提取 + Write-Host " 正在提取文件..." + & $7zPath x "-o$gitDir" "-y" "$dl7z" | Out-Null + + if (Test-Path "$gitDir\bin\git.exe") { + Write-Host " 提取成功: git.exe 已存在" + } else { + Write-Host " 警告: 提取可能失败,检查目录内容" + Get-ChildItem $gitDir -Recurse -File | Select-Object -First 10 | ForEach-Object { Write-Host " $_" } + } +} else { + Write-Host " 错误: dl.7z 未找到,请重新下载" + Write-Host " 运行: scoop cache rm git; scoop download git" + exit 1 +} + +# 4. 创建 Git shims +Write-Host "4. 创建 Git shims..." + +# git shim +$gitShim = "$shimsDir\git.shim" +$gitExe = "$gitDir\bin\git.exe" +if (Test-Path $gitExe) { + Set-Content $gitShim "path = `"$gitExe`"" + Write-Host " 创建 git.shim" + + # 复制 shim 可执行文件 + $shimExe = "$shimsDir\git.exe" + if (-not (Test-Path $shimExe)) { + Copy-Item "$shimsDir\7z.exe" $shimExe -ErrorAction SilentlyContinue + if (Test-Path $shimExe) { + Write-Host " 创建 git.exe shim" + } + } +} else { + Write-Host " 错误: git.exe 未找到" +} + +# sh (bash) shim +$shShim = "$shimsDir\sh.shim" +$shExe = "$gitDir\bin\sh.exe" +if (Test-Path $shExe) { + Set-Content $shShim "path = `"$shExe`"" + Write-Host " 创建 sh.shim" +} else { + Write-Host " 警告: sh.exe 未找到" +} + +# git-bash shim +$gitBashShim = "$shimsDir\git-bash.shim" +$gitBashExe = "$gitDir\git-bash.exe" +if (Test-Path $gitBashExe) { + Set-Content $gitBashShim "path = `"$gitBashExe`"" + Write-Host " 创建 git-bash.shim" +} + +# 5. 更新 PATH 并测试 +Write-Host "5. 测试安装..." +$env:Path = "$shimsDir;$env:Path" + +Write-Host "`n测试命令:" +try { + $gitVersion = git --version 2>$null + if ($gitVersion) { + Write-Host " ✓ git: $gitVersion" + } else { + Write-Host " ✗ git: 命令失败" + Write-Host " 临时使用: $gitExe" + } +} catch { + Write-Host " ✗ git: 错误: $_" +} + +try { + $7zVersion = 7z --help 2>$null | Select-Object -First 1 + if ($7zVersion) { + Write-Host " ✓ 7z: 可用" + } +} catch { + Write-Host " ✗ 7z: 错误" +} + +Write-Host "`n修复完成!" +Write-Host "如果命令仍然不可用,请重启终端或运行:" +Write-Host " `$env:Path = `"$shimsDir;`$env:Path`"" +Write-Host "`n然后测试: git --version" \ No newline at end of file diff --git a/fix-scoop.ps1 b/fix-scoop.ps1 new file mode 100644 index 0000000..7f9a5ce --- /dev/null +++ b/fix-scoop.ps1 @@ -0,0 +1,69 @@ +Write-Host "Fixing Scoop shims for git..." + +$scoopDir = "E:\Scoop" +$gitDir = "E:\Scoop\apps\git\2.53.0" +$shimsDir = "E:\Scoop\shims" + +Write-Host "1. Checking git installation..." +if (-not (Test-Path "$gitDir\bin\git.exe")) { + Write-Host "ERROR: git.exe not found at $gitDir\bin\git.exe" + exit 1 +} +Write-Host " git.exe found" + +Write-Host "2. Fixing sh.shim..." +$shShim = "$shimsDir\sh.shim" +if (Test-Path $shShim) { + $content = Get-Content $shShim + if ($content -match "current") { + Set-Content $shShim 'path = "E:\Scoop\apps\git\2.53.0\bin\sh.exe"' + Write-Host " Updated sh.shim to use absolute path" + } else { + Write-Host " sh.shim already uses absolute path" + } +} else { + Write-Host " ERROR: sh.shim not found" +} + +Write-Host "3. Creating git shim..." +$gitShim = "$shimsDir\git.shim" +if (-not (Test-Path $gitShim)) { + Set-Content $gitShim 'path = "E:\Scoop\apps\git\2.53.0\bin\git.exe"' + Write-Host " Created git.shim" +} else { + Write-Host " git.shim already exists" +} + +Write-Host "4. Creating git.exe shim executable..." +$gitExe = "$shimsDir\git.exe" +if (-not (Test-Path $gitExe)) { + $sourceShim = "$shimsDir\7z.exe" + if (Test-Path $sourceShim) { + Copy-Item $sourceShim $gitExe + Write-Host " Copied 7z.exe to git.exe" + } else { + Write-Host " ERROR: No source shim executable found" + } +} else { + Write-Host " git.exe already exists" +} + +Write-Host "5. Testing git command..." +$env:Path = "$shimsDir;$env:Path" +try { + git --version + Write-Host " SUCCESS: git command works!" +} catch { + Write-Host " WARNING: git command may not work directly, but you can use full path: $gitDir\bin\git.exe" +} + +Write-Host "6. Testing sh command..." +try { + sh --version + Write-Host " SUCCESS: sh command works!" +} catch { + Write-Host " WARNING: sh command may not work" +} + +Write-Host "`nFix complete. If commands still don't work, try restarting your terminal or running:" +Write-Host " . `$env:Path = `"E:\Scoop\shims;`$env:Path`"" \ No newline at end of file diff --git a/init-git.ps1 b/init-git.ps1 new file mode 100644 index 0000000..e9eb059 --- /dev/null +++ b/init-git.ps1 @@ -0,0 +1,49 @@ +Write-Host "初始化 Git 仓库..." + +# 检查是否已经是 Git 仓库 +if (Test-Path .git) { + Write-Host "✓ 已经是 Git 仓库" + git status + exit 0 +} + +# 初始化 Git +Write-Host "初始化 Git 仓库..." +git init + +# 配置用户信息(如果未设置) +$gitUser = git config --global user.name +$gitEmail = git config --global user.email + +if (-not $gitUser) { + Write-Host "提示: 请设置 Git 用户信息:" + Write-Host " git config --global user.name 'Your Name'" + Write-Host " git config --global user.email 'your.email@example.com'" +} + +# 添加文件 +Write-Host "添加文件到暂存区..." +git add . + +# 检查是否有文件被添加 +$staged = git status --porcelain +if (-not $staged) { + Write-Host "警告: 没有文件被添加到暂存区,可能是 .gitignore 过滤了所有文件" + Write-Host "查看当前状态:" + git status + exit 0 +} + +# 创建初始提交 +Write-Host "创建初始提交..." +git commit -m "Initial commit: React phone apps project" + +Write-Host "`n✓ Git 仓库初始化完成!" +Write-Host "当前分支: $(git branch --show-current)" +Write-Host "提交记录:" +git log --oneline -3 + +Write-Host "`n下一步:" +Write-Host "1. 连接到远程仓库: git remote add origin " +Write-Host "2. 推送到远程: git push -u origin main" +Write-Host "3. 查看状态: git status" \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..f67aa56 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "react-phone-apps", + "private": true, + "scripts": { + "dev": "pnpm -r --filter './apps/**' dev", + "build": "pnpm -r --filter './apps/**' build", + "lint": "pnpm -r lint" + }, + "engines": { + "node": ">=18", + "pnpm": ">=8" + } +} diff --git a/packages/.gitkeep b/packages/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..9b60ae1 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,9 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e9b0dad --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - 'apps/*' + - 'packages/*' diff --git a/test.ps1 b/test.ps1 new file mode 100644 index 0000000..164b588 --- /dev/null +++ b/test.ps1 @@ -0,0 +1,35 @@ +Write-Host "Testing git installation..." +$gitPath = "E:\Scoop\apps\git\2.53.0\bin\git.exe" +if (Test-Path $gitPath) { + Write-Host "Git executable exists at: $gitPath" + & $gitPath --version +} else { + Write-Host "Git executable not found" +} + +Write-Host "`nChecking current symlink..." +$currentLink = "E:\Scoop\apps\git\current" +if (Test-Path $currentLink) { + Write-Host "Current link exists" + $target = Get-Item $currentLink | Select-Object -ExpandProperty Target + Write-Host "Current link target: $target" +} else { + Write-Host "Current link does not exist" +} + +Write-Host "`nChecking sh.exe..." +$shPath = "E:\Scoop\apps\git\current\bin\sh.exe" +if (Test-Path $shPath) { + Write-Host "sh.exe exists at: $shPath" +} else { + Write-Host "sh.exe not found at: $shPath" +} + +Write-Host "`nChecking shim..." +$shimPath = "E:\Scoop\shims\sh.shim" +if (Test-Path $shimPath) { + Write-Host "sh.shim exists" + Get-Content $shimPath +} else { + Write-Host "sh.shim not found" +} \ No newline at end of file diff --git a/修复Git安装步骤.txt b/修复Git安装步骤.txt new file mode 100644 index 0000000..dd057c7 --- /dev/null +++ b/修复Git安装步骤.txt @@ -0,0 +1,61 @@ +修复 Scoop Git 安装步骤 + +请在新终端(CMD 或 PowerShell)中逐条执行以下命令: + +1. 检查并修复 7zip shim +打开文件 E:\Scoop\shims\7z.shim,确保内容为: +path = "E:\Scoop\apps\7zip\25.01\7z.exe" + +如果不同,请编辑或运行: +echo path = "E:\Scoop\apps\7zip\25.01\7z.exe" > "E:\Scoop\shims\7z.shim" + +2. 创建 7zip current 符号链接 +以管理员身份运行 CMD,执行: +mklink /J "E:\Scoop\apps\7zip\current" "E:\Scoop\apps\7zip\25.01" + +如果无法创建符号链接,可以直接跳过此步,但需确保后续使用绝对路径。 + +3. 手动提取 Git 安装包 +在 PowerShell 中执行: +& "E:\Scoop\apps\7zip\25.01\7z.exe" x "E:\Scoop\apps\git\2.53.0.2\dl.7z" -o"E:\Scoop\apps\git\2.53.0.2" -y + +等待提取完成。 + +4. 检查提取结果 +执行: +dir "E:\Scoop\apps\git\2.53.0.2" + +应该看到 bin、cmd、etc 等目录。 + +5. 创建 Git shim 文件 +创建 E:\Scoop\shims\git.shim: +echo path = "E:\Scoop\apps\git\2.53.0.2\bin\git.exe" > "E:\Scoop\shims\git.shim" + +创建 shim 可执行文件: +copy "E:\Scoop\shims\7z.exe" "E:\Scoop\shims\git.exe" + +6. 创建 sh (bash) shim +echo path = "E:\Scoop\apps\git\2.53.0.2\bin\sh.exe" > "E:\Scoop\shims\sh.shim" + +7. 创建 git-bash shim +echo path = "E:\Scoop\apps\git\2.53.0.2\git-bash.exe" > "E:\Scoop\shims\git-bash.shim" + +8. 更新 PATH 环境变量(临时) +在 PowerShell 中执行: +$env:Path = "E:\Scoop\shims;$env:Path" + +9. 测试安装 +执行: +git --version +sh --version + +如果显示版本信息,则安装成功。 + +10. 永久添加 PATH(可选) +将 E:\Scoop\shims 添加到系统 PATH 环境变量: +[Environment]::SetEnvironmentVariable("Path", "E:\Scoop\shims;" + [Environment]::GetEnvironmentVariable("Path", "User"), "User") + +重启终端使更改生效。 + +如果仍有问题,可以临时使用完整路径: +E:\Scoop\apps\git\2.53.0.2\bin\git.exe \ No newline at end of file