Appearance
前端 Monorepo 架构
包结构与依赖关系
@shice/composables → @shice/stores → @shice/api → @shice/shared
↓ ↓ ↓ ↓
业务逻辑Hook Pinia状态管理 HTTP客户端 类型+工具依赖方向:单向自上而下,禁止反向依赖。
@shice/shared — 类型定义层
文件:frontend/packages/shared/src/
- 纯 TypeScript,零运行时依赖,零 DOM API
- 类型按领域拆分:
user.ts,person.ts,strategy.ts,chat.ts,subscription.ts,api.ts,selfProfile.ts ApiResponse<T>和PaginatedResponse<T>作为所有 API 响应的泛型容器ChatRequest.locale?: string— 后端回复语言SelfProfileFields— 用户自我画像(summary / goals / communicationStyle / boundaries / context)- 常量集中管理:
RELATIONSHIP_TYPE_LABELS,SCENARIO_TYPE_LABELS,IMPORTANCE_LABELS - 工具函数:
formatDate,truncate,generateId,debounce
跨端注意:绝不引入浏览器/Node.js 特有 API,UniApp 可直接 workspace 引用。
@shice/api — HTTP 客户端层
文件:frontend/packages/api/src/
- 与后端契约:REST 路径与请求/响应形状以 api_routes.md(对照
internal/handler/router.go)为准;流式对话见 sse.md。 - 基于 axios 封装,按模块拆分:
auth.ts(含getSelfProfile/updateSelfProfile)、person.ts,chat.ts,strategy.ts,subscription.ts - Base URL 通过
configureApiClient({ baseUrl })动态注入 - Token 自动刷新:401 拦截器 →
/auth/refresh→ 重试 streamChat(data: ChatRequest, ...)— POST + fetch ReadableStream 方式实现 SSE- 策略 API 类型已带
locale字段,前端页面接入时需传入
@shice/stores — 状态管理层
文件:frontend/packages/stores/src/
useUserStore:登录/注册 + token 持久化 +restoreSession()+isPremiumusePersonStore:CRUD + 分页 +personMapO(1) 查找useChatStore:乐观更新 + 流式对话 +streaming/streamingContent/selectedModel+currentRequestLocale()从localStorage('shice_locale')读取
跨端注意:localStorage 需在 UniApp 中替换为 uni.setStorageSync(抽取 StorageAdapter)。
@shice/composables — 业务逻辑层
useAuth:认证流程usePersonManager:人物 CRUD + 搜索 + 分页- 不含 UI 操作;后续扩展
useChat,useStrategy
Web 应用 (apps/web)
技术选型
| 依赖 | 用途 |
|---|---|
| Vue 3 ^3.5 | 核心框架 |
| Naive UI ^2.40 | 组件库 |
| vue-i18n 9 | 中英界面国际化 |
| vue-router 4 | SPA 路由 |
| UnoCSS | 原子 CSS |
| Vite 6 | 构建工具 |
路由设计
/login, /register → guest only
/ → MainLayout (requiresAuth)
├── / → DashboardView
├── /persons → PersonListView
├── /persons/:id → PersonDetailView
├── /chat → ChatView
├── /chat/:sessionId → ChatView
└── /settings → SettingsView布局
- 桌面端:左侧 NLayoutSider(220px,可折叠)+ 顶栏(暗色切换 + 语言切换 NSelect + 用户下拉)
- 移动端:汉堡菜单 + Teleport 侧栏遮罩
国际化
i18n.ts:createI18n({ legacy: false });locale 初始值来自localStorage('shice_locale')App.vuewatch locale → 同步localStorage+ Naive UI locale (zhCN/enUS)MainLayout顶栏 NSelect 切换语言locales/zh.json/en.json:nav / chat / settings / common
核心页面
- DashboardView:统计卡片 + 快速操作 + 最近动态 Timeline
- PersonListView:搜索 + 过滤 + 卡片列表 + 新建 Modal
- PersonDetailView:头部信息 + 详情卡片 + 操作区
- ChatView:左右分栏(会话列表 + 聊天区);流式打字机 + 停止生成 + 自动滚动
- 画像与档案更新提案:SSE 结束帧若含
profileProposal,由useChatStore暂存为pendingProfileProposal。incomplete_self/incomplete_person(系统判断「我的画像」或关联人脉档案偏空):先弹出居中引导(文案见 i18nchat.profileCenterNudge*),用户点「去更新」后打开ProfileProposalModal;弹窗内会 请求GET /auth/self-profile或人脉详情,把 该侧全部可编辑字段 展开为一列表格(宽屏约min(720px, 96vw)),并与 LLM 返回的 patch 按 key 合并;缺失路径下每一行默认勾选,可「全不选」或单项取消后再「应用所选」;写入仍走既有PUT self-profile/PUT persons/:id。refinement:不经过居中引导,在输入区上方显示 底部提示条 + 约 30 秒倒计时,可直接打开同一确认弹窗;弹窗内容为后端给出的 patch 列表(默认全选)。
- 组件:
views/chat/ChatView.vue、components/chat/ProfileProposalModal.vue;文案键chat.profileProposal*、chat.profileCenterNudge*、chat.profileProposalFullFormHint、chat.profileProposalLoading。 - SettingsView:账号 Tab(订阅状态 / 外观 / 关于)+ 我的画像 Tab(SelfProfile 表单)
Vite 配置
/api代理到http://127.0.0.1:8080(与vite.config.ts一致)@/路径别名指向src/- 生产构建在
ELECTRON=1时使用base: './',供桌面包加载本地index.html server.strictPort: true且端口3000,与apps/desktop开发态固定打开http://127.0.0.1:3000对齐(勿同时开两个占 3000 的 dev)
Electron 桌面壳 (apps/desktop)
- 开发:仓库根下
cd frontend && pnpm dev:desktop(concurrently启动dev:web+ Electron;等待3000 就绪) - 构建安装包:
pnpm build:desktop或cd apps/desktop && pnpm pack(解包目录在release/) - 路由:
file://打开本地文件时自动使用 hash history(router/index.ts);开发态走http仍为 history 模式 - 预加载:
preload.cjs暴露window.shiceElectron.platform;OAuth / 自动更新等仍可按 ui_design_notes.md 扩展