Refactor code structure for improved readability and maintainability

This commit is contained in:
duanshuwen
2026-05-26 14:53:24 +08:00
parent b05d5a72cd
commit fa76435e38
6 changed files with 1309 additions and 483 deletions

View File

@@ -1,456 +0,0 @@
# YGChatCS Vue3 Web 技术栈改造计划
## 文档状态
- 状态:规划中
- 日期2026-05-25
- 目标技术栈Vue 3 + Pinia + TypeScript + Tailwind CSS + Vite
- 当前技术栈uni-app 3 + Vue 3 + Pinia + SCSS + Vite
## 背景
当前项目是基于 uni-app 的多端应用,配置重点面向微信小程序,业务核心是酒店/文旅场景的 AI 聊天助手,并包含发现内容、商品/房型详情、订单、工单服务、快捷预订、WebView 桥接等模块。
本次改造目标是逐步抛弃 uni-app 技术栈,转向标准 Vue3 Web 技术栈。由于项目中大量能力依赖 `uni.*` 运行时 API、uni 页面生命周期、`pages.json` 路由配置、小程序专属能力和 uni 组件标签,不能采用一次性全量替换。推荐采用“新 Web 工程并行建设 + 功能分阶段迁移 + 旧项目作为回归基线”的方式,降低代码缺失和功能缺失风险。
## 改造原则
1. 旧项目先保持可运行,作为行为基线和回归对照。
2. 新 Vue3 Web 工程并行建设,优先迁移基础设施,再迁移业务页面。
3. 每个阶段都必须有可验证产物,避免长期处于不可运行状态。
4. 迁移按业务闭环纵向切片,不按“所有组件一次性搬完”的横向方式推进。
5. 先封装平台适配层,再逐步替换页面中的 `uni.*` 调用。
6. 微信小程序专属能力必须单独评估 Web/H5 替代方案,不能默认等价迁移。
7. TypeScript 类型补齐随迁移同步推进,不把 JS 全量机械改名为 TS 后再补类型。
## 当前项目关键依赖面
### 入口与配置
- `src/main.js`:创建 uni-app Vue 应用,注册 Pinia 和 `pinia-plugin-unistorage`
- `src/App.vue`应用生命周期负责服务地址初始化、token 刷新、网络恢复重载。
- `src/pages.json`uni-app 页面、分包和页面样式配置。
- `src/manifest.json`:小程序 appid、权限、插件和平台配置。
- `vite.config.js`:当前依赖 `@dcloudio/vite-plugin-uni`
### 核心业务模块
- `src/pages/ChatMain/ChatMainList/index.vue`聊天主流程包含消息列表、WebSocket 初始化、消息发送、工具卡渲染和滚动控制。
- `src/request/api/AgentChatStream.js`:微信小程序分块/流式请求封装,依赖 `uni.request({ enableChunked: true })`
- `src/utils/WebSocketManager.js`:跨端 WebSocket 管理器,当前优先使用 `uni.connectSocket`
- `src/request/base/request.js`:统一请求封装,依赖 `uni.request`、token、客户端配置和位置请求头。
- `src/manager/LoginManager.js``src/hooks/useGoLogin.js`:登录、刷新 token、跳转登录、登录成功事件。
- `src/pages/goods/``src/pages-booking/``src/pages-order/``src/pages-service/`:商品、预订、订单和工单业务链路。
### 高风险 uni 能力
| 当前能力 | 使用场景 | Web 改造方向 | 风险 |
| -------------------------------------- | ------------------- | -------------------------------- | ---- |
| `uni.navigateTo/reLaunch/navigateBack` | 页面跳转 | Vue Router 服务封装 | 中 |
| `uni.request/uploadFile` | HTTP 请求、文件上传 | `fetch` 或 Axios + 上传封装 | 中 |
| `uni.$emit/$on/$off` | 跨组件事件 | `mitt` 或 typed event bus | 中 |
| `uni.showToast/showModal/showLoading` | 用户反馈 | UI 组件库或自研反馈服务 | 低 |
| `uni.connectSocket` | AI 聊天实时通信 | 浏览器原生 `WebSocket` | 高 |
| `uni.request enableChunked` | AI 流式回复 | `fetch + ReadableStream` 或 SSE | 高 |
| `uni.login` | 微信登录 | 微信 Web 授权或后端 H5 登录方案 | 高 |
| `uni.requestPayment` | 支付 | 微信 JS-SDK/H5 支付/后端支付跳转 | 高 |
| `uni.chooseImage` | 图片选择上传 | `<input type="file">` + 上传服务 | 中 |
| `uni.saveImageToPhotosAlbum` | 保存图片 | 浏览器下载能力,移动端受限 | 中 |
| `uni.getLocation/openLocation` | 定位和地图 | Geolocation API + 地图 URL/SDK | 中 |
| `uni.createSelectorQuery` | 尺寸测量 | DOM API、ResizeObserver | 中 |
| `uni.onKeyboardHeightChange` | 输入框键盘适配 | 视口监听、CSS env、安全区适配 | 高 |
| `<view>/<text>/<image>/<scroll-view>` | 页面模板 | HTML 标签或基础组件替换 | 中 |
| `<web-view>` | 内嵌页面 | `iframe` + `postMessage` 协议 | 中 |
| `<canvas>` + uni canvas API | 二维码/图片 | 浏览器 Canvas 或第三方库 | 中 |
## 目标架构
### 推荐目录结构
建议在仓库内新建 `web/` 目录承载新 Web 应用,旧 uni-app 代码暂时保留:
```text
web/
src/
app/
main.ts
router.ts
pinia.ts
assets/
components/
constants/
features/
chat/
discovery/
goods/
booking/
order/
service/
bridge/
services/
http/
auth/
router/
storage/
event-bus/
feedback/
websocket/
stream/
browser/
stores/
styles/
types/
```
### 基础技术选型
| 能力 | 建议方案 |
| ---------- | --------------------------------------------------- |
| 框架 | Vue 3 + Composition API |
| 类型 | TypeScript严格模式分阶段开启 |
| 状态管理 | Pinia |
| 状态持久化 | `pinia-plugin-persistedstate` 或自研 storage plugin |
| 路由 | Vue Router |
| 样式 | Tailwind CSS + 少量全局 CSS 变量 |
| 请求 | Axios 或标准 `fetch` 封装,二选一后统一使用 |
| 事件总线 | `mitt`,建议加类型约束 |
| WebSocket | 浏览器原生 `WebSocket` |
| 流式响应 | `fetch + ReadableStream`,必要时兼容 SSE |
| 构建 | Vite |
| 测试 | Vitest + Vue Test Utils关键流程补 Playwright |
## 分阶段改造计划
### 阶段 0迁移盘点和基线冻结
目标:明确迁移范围,冻结旧项目行为基线。
任务:
- 梳理 `src/pages.json`,生成旧路由到 Vue Router 的映射表。
- 统计所有 `uni.*``wx.*``@dcloudio/uni-app` 生命周期和 uni 组件标签使用点。
- 按业务域列出功能清单聊天、发现、商品、预订、订单、工单、WebView、图片、支付、定位。
- 标记每个页面和组件的迁移策略:保留、合并、重写、废弃、延后。
- 为核心流程录制或截图旧项目行为,作为新版本验收依据。
验收标准:
- 有完整的路由映射表。
- 有完整的 uni API 替换清单。
- 每个业务模块有明确迁移优先级。
- 旧项目可继续 `yarn build:mp-weixin` 或对应环境构建。
### 阶段 1搭建 Vue3 Web 工程骨架
目标:建立新 Web 应用的最小可运行基础。
任务:
- 新建 `web/` 工程,配置 Vue3、TypeScript、Vite、Pinia、Vue Router、Tailwind CSS。
- 配置 `@/` 路径别名、环境变量、构建脚本和基础目录结构。
- 建立全局样式入口,迁移必要字体、主题色和基础 CSS reset。
- 建立基础布局:移动端主容器、安全区、页面滚动策略。
- 增加最小健康页和 404 页,验证路由可用。
验收标准:
- `web` 工程可以独立安装、启动和构建。
- TypeScript、Tailwind、Router、Pinia 都可用。
- 新项目不依赖 `@dcloudio/*` 包。
### 阶段 2建设平台适配层
目标:先统一替代 uni 运行时能力,避免业务页面直接依赖浏览器 API。
任务:
- 实现 `services/router`:封装 `push``replace``back``reLaunch` 等语义。
- 实现 `services/http`:封装 baseUrl、clientId、token、位置请求头、424 登出处理。
- 实现 `services/storage`:替代同步 storage API并兼容 token 持久化。
- 实现 `services/event-bus`:替代 `uni.$emit/$on/$off`,事件名沿用现有常量。
- 实现 `services/feedback`:封装 toast、modal、loading。
- 实现 `services/browser`:封装图片选择、下载、定位、电话、复制等浏览器能力。
- 实现 `services/auth`:封装 token、刷新、登出、登录跳转。
验收标准:
- 请求层、事件、路由、反馈、存储均有统一入口。
- 页面迁移时不得新增裸 `fetch`、裸 `localStorage`、裸 `window.location` 作为业务逻辑入口。
- 424 登出逻辑在新请求层可复用。
### 阶段 3迁移配置、Store 和 API
目标:先迁移非 UI 业务基础,让后续页面迁移有稳定依赖。
任务:
- 迁移 `src/constant/``web/src/constants/`,补充 TypeScript 类型。
- 迁移 `client-configs.json` 和当前客户端配置读取逻辑。
- 迁移 Pinia store`app``location``picture``selectedDate`
- 替换 `pinia-plugin-unistorage` 为 Web 持久化方案。
验收标准:
- 新 Web 应用能读取客户端配置和服务地址。
- token 可以持久化、刷新和清除。
- 普通业务 API 可以通过新请求层调用。
- API 模块不再依赖 `uni.request`
### 阶段 4迁移 AI 聊天核心链路
目标:优先打通项目最高价值、最高风险的聊天闭环。
任务:
- 拆分聊天主页面,避免继续维护单个超大组件。
- 迁移消息模型:`MessageRole``MessageType``CompName``Command`
- 改造 `WebSocketManager` 为浏览器原生 WebSocket 实现,保留重连、队列、心跳和销毁逻辑。
- 改造流式回复:用 `fetch + ReadableStream` 或 SSE 替代 `enableChunked`
- 迁移消息列表、用户消息、AI 消息、加载态、停止生成、超时处理。
- 迁移工具卡分发逻辑:快捷预订、发现卡、地图卡、反馈卡、商品图文卡、问卷卡、长文本卡。
- 重做滚动到底、输入框键盘、移动端视口适配。
验收标准:
- 用户可以进入首页聊天。
- 可以发送普通文本消息。
- 可以收到流式 AI 回复。
- 可以渲染至少一类 `toolCall` 卡片。
- 可以停止当前回复。
- WebSocket 断线重连和消息发送失败有可感知反馈。
- 登录失效后能跳转登录或触发登录流程。
### 阶段 5迁移首页和发现模块
目标:完成用户进入应用后的主要浏览路径。
任务:
- 迁移首页壳、顶部导航、欢迎区、Tab 切换。
- 迁移发现页内容流、快捷问题、卡片轮播和定位逻辑。
- 迁移快捷问题触发聊天事件的逻辑。
-`scroll-view` 改造成浏览器滚动容器。
-`uni.createSelectorQuery` 相关逻辑替换为 DOM API 或 ResizeObserver。
验收标准:
- 首页视觉结构和旧版本一致或有明确设计调整。
- 发现页可以正常加载数据。
- 快捷问题可以切换到聊天并发送内容。
- 移动端滚动不会和输入框区域冲突。
### 阶段 6迁移商品、预订和订单闭环
目标:完成从商品详情到下单、支付前、订单查看的业务闭环。
任务:
- 迁移商品/房型详情、图片相册、设施、套餐、日期选择。
- 迁移预订确认页、联系人、入住人、数量、金额计算。
- 迁移订单列表和订单详情。
- 迁移退款、取消、订单状态展示。
- 评估支付能力Web H5 支付、微信 JS-SDK 支付或后端支付跳转。
验收标准:
- 商品详情可打开并展示完整信息。
- 酒店类商品日期选择和预订参数正确。
- 可以提交订单或完成支付前置参数生成。
- 订单列表和详情可查看。
- 支付能力有明确环境方案和失败兜底。
### 阶段 7迁移工单、反馈、快捷入口和桥接能力
目标:补齐非聊天核心业务能力。
任务:
- 迁移工单列表和工单卡片。
- 迁移服务呼叫、反馈表单、图片上传。
- 迁移快捷入口列表和快捷入口卡片。
- 将 WebView 改造为 iframe 或外链跳转。
- 重建桥接协议:`postMessage`、token 注入、图片上传回传、保存图片。
验收标准:
- 工单、反馈、快捷入口可完成主要操作。
- 图片选择和上传在 Web 环境可用。
- 外部 H5 页面能通过新桥接协议与主应用通信。
- 保存图片或下载能力有浏览器兼容说明。
### 阶段 8Tailwind 样式迁移和 UI 收敛
目标:逐步替换旧 SCSS 工具类,建立 Web 版统一视觉系统。
任务:
-`src/uni.scss` 中主题变量迁移到 Tailwind config 和 CSS variables。
- 将常用工具类映射到 Tailwind避免一次性改完所有样式。
- 优先改造新迁移页面中的 SCSS旧代码不做无意义格式化。
- 移除 `rpx``page`、小程序专属样式语义。
- 建立移动端安全区、底部固定栏、弹窗、滚动容器的统一样式模式。
验收标准:
- 新 Web 页面主要使用 Tailwind 和少量局部 CSS。
- 不再依赖 `src/static/scss` 的 uni 工具类。
- 移动端主流程无明显文字溢出、遮挡、滚动穿透。
### 阶段 9测试、灰度和旧栈下线
目标:在新版本稳定后再删除旧 uni-app 技术栈。
任务:
- 为请求层、事件总线、WebSocket、流式解析补单元测试。
- 为聊天、商品、订单、登录失效等核心流程补端到端测试。
- 新旧版本并行验收,建立问题清单。
- 小范围灰度新 Web 版本,监控白屏、接口错误、聊天失败、支付失败。
- 稳定后冻结旧 uni-app 项目。
- 最后删除 `@dcloudio/*` 依赖、`src/uni_modules/``pages.json``manifest.json`、条件编译代码和废弃适配逻辑。
验收标准:
- 新版本核心功能覆盖旧版本核心路径。
- 回归问题关闭或有明确延期说明。
- 旧 uni-app 代码零活跃依赖后再移除。
## 建议迁移优先级
| 优先级 | 模块 | 原因 |
| ------ | ---------------------------------- | ------------------------ |
| P0 | 平台适配层、配置、请求、token | 所有业务依赖,必须先稳定 |
| P0 | AI 聊天主链路 | 产品核心价值,风险最高 |
| P1 | 首页、发现、快捷问题 | 用户入口和聊天联动 |
| P1 | 商品详情、预订、订单 | 核心商业闭环 |
| P2 | 工单、反馈、快捷入口 | 重要但可后续分批迁移 |
| P2 | WebView 桥接、图片保存 | 依赖外部页面和浏览器兼容 |
| P3 | 非核心 demo、README 示例、废弃组件 | 最后清理 |
## 路由迁移初稿
| uni-app 路由 | Vue Router 建议路由 | 说明 |
| -------------------------------------------------- | ---------------------- | ------------------------------ |
| `/pages/index/index` | `/` | 首页和聊天主入口 |
| `/pages/login/index` | `/login` | 登录页 |
| `/pages/goods/index` | `/goods/:commodityId?` | 商品/房型详情 |
| `/pages/goods/album/index` | `/goods/album` | 商品相册 |
| `/pages/webview/index` | `/bridge/webview` | iframe 或外链桥接 |
| `/pages/ChatMain/ChatLongAnswer/index` | `/chat/long-answer` | 长回答页 |
| `/pages/ChatModule/LongTextGuideCardPreview/guide` | `/chat/guide/guide` | 长文本 guide 详情 |
| `/pages/ChatModule/LongTextGuideCardPreview/poi` | `/chat/guide/poi` | 长文本 poi 详情 |
| `/pages/ChatModule/LongTextGuideCardPreview/route` | `/chat/guide/route` | 长文本 route 详情 |
| `/pages/ChatModule/LongTextGuideCardPreview/photo` | `/chat/guide/photo` | 长文本 photo 详情 |
| `/pages/ChatMain/NoticeMessage/detail` | `/notice/:id?` | 通知详情 |
| `/pages-order/order/list` | `/orders` | 订单列表 |
| `/pages-order/order/detail` | `/orders/:orderId` | 订单详情 |
| `/pages-service/order/list` | `/service-orders` | 工单列表 |
| `/pages-quick/list` | `/quick` | 快捷入口 |
| `/pages-booking/index` | `/booking` | 预订确认 |
| `/pages-bridge/UploadImage` | `/bridge/upload-image` | 图片上传桥接,可被新协议替代 |
| `/pages-bridge/SaveImage` | `/bridge/save-image` | 保存图片桥接,可被下载能力替代 |
## 关键验收清单
### 聊天
- 可以创建/恢复会话。
- 可以发送文本和快捷指令。
- AI 回复支持流式展示。
- 工具卡按 `componentName` 正确渲染。
- 长文本卡可以进入详情页。
- 停止生成、超时、失败、重试都有明确状态。
- WebSocket 断线重连不丢当前消息状态。
### 登录与权限
- 无 token 时跳转登录。
- refresh token 可自动刷新。
- HTTP 424 会清除 token 并触发登出。
- 登录成功事件能刷新聊天、通知等依赖模块。
- 微信 Web/H5 授权方案明确。
### 商品与订单
- 商品详情、相册、套餐、设施、日期选择正常。
- 预订参数、联系人、入住人、金额计算正确。
- 订单列表、订单详情、退款状态正常。
- 支付方案在目标平台可用。
### Web 能力
- 图片选择和上传可用。
- 地理位置授权失败有降级提示。
- 电话能力在 Web 上有明确降级方式。
- WebView/iframe 桥接协议可用。
- 保存图片能力在浏览器限制下有替代方案。
### UI 与兼容
- 移动端首屏高度、底部输入框、安全区正确。
- 滚动区域不穿透,聊天列表能稳定滚到底。
- 键盘弹出时输入区不遮挡。
- 主要页面在常见移动端 viewport 下无明显错位。
## 风险和缓解措施
| 风险 | 影响 | 缓解措施 |
| ---------------------------------- | ---- | ---------------------------------------------------- |
| 一次性替换导致功能缺失 | 高 | 新旧并行,按业务闭环迁移 |
| 聊天流式协议在 Web 不兼容 | 高 | 优先做流式原型,确认后端支持 `ReadableStream` 或 SSE |
| 微信登录/支付无法等价迁移 | 高 | 提前确认目标是 H5、公众号、企业微信还是普通浏览器 |
| 键盘和滚动体验退化 | 高 | 聊天页单独验收移动端视口和输入行为 |
| Tailwind 全量替换成本过高 | 中 | 页面迁移时渐进替换,不做纯样式大爆炸 |
| 旧 SCSS 工具类和 Tailwind 混用混乱 | 中 | 建立映射规则和废弃清单 |
| JS 改 TS 产生大量类型债 | 中 | 按模块补类型,优先 API、store、消息模型 |
| 外部 H5 桥接协议变化影响存量页面 | 中 | 保留兼容层,先支持旧 query/token 方式 |
## 不建议的做法
- 不建议在当前 `src/` 中直接删除 uni-app 依赖后边报错边修。
- 不建议先全量把 `.js` 改成 `.ts`,再统一解决类型错误。
- 不建议先全量替换 `<view>``<text>``<image>`,再处理业务逻辑。
- 不建议一开始重写全部 SCSS 到 Tailwind。
- 不建议在未确认微信 Web 登录/支付方案前删除旧小程序能力。
- 不建议迁移期间手工修改 `dist/` 构建产物。
## 推荐里程碑
### M1新 Web 基座可运行
- Web 工程启动、构建正常。
- Router、Pinia、Tailwind、请求层、事件总线、反馈服务可用。
### M2聊天主链路可用
- 首页聊天可打开。
- 文本发送、流式回复、工具卡渲染、停止生成可用。
### M3首页和发现可用
- 首页、发现页、快捷问题和聊天联动完成。
### M4交易闭环可用
- 商品详情、预订确认、订单列表、订单详情完成。
- 支付方案完成或明确接入边界。
### M5辅助模块补齐
- 工单、反馈、快捷入口、WebView/桥接、图片能力完成。
### M6旧 uni-app 下线
- 新版本核心路径验收通过。
- 旧代码无活跃依赖。
- 移除 uni-app 依赖和废弃文件。
## 后续建议
下一步建议先执行阶段 0输出三份更细的落地清单
1. `docs/ROUTE_MIGRATION_MAP.md`:完整路由映射。
2. `docs/UNI_API_REPLACEMENT_MATRIX.md`uni API 替换矩阵。
3. `docs/FEATURE_MIGRATION_CHECKLIST.md`:业务功能迁移验收清单。
完成这三份清单后,再开始搭建 `web/` 工程,能最大限度减少遗漏。

View File

@@ -1,17 +1,17 @@
{
"name": "ygchatcs-web",
"version": "0.1.0",
"name": "nianxx-h5",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview --host 0.0.0.0",
"typecheck": "vue-tsc --noEmit",
"test": "node --test src/**/*.test.ts"
},
"dependencies": {
"@vitejs/plugin-vue": "5.1.0",
"@vitejs/plugin-vue": "^6.0.7",
"axios": "^1.16.1",
"mitt": "^3.0.1",
"pinia": "^3.0.3",
@@ -24,11 +24,14 @@
"@tailwindcss/vite": "^4.1.12",
"@tsconfig/node20": "^20.1.6",
"@types/node": "^22.13.14",
"@vitejs/plugin-legacy": "^8.0.2",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/tsconfig": "^0.7.0",
"concurrently": "^9.2.1",
"tailwindcss": "^4.1.12",
"typescript": "^5.8.3",
"vconsole": "^3.15.1",
"vite": "5.2.8",
"vue-tsc": "^2.2.10"
},
"packageManager": "yarn@1.22.22"
}
}

0
src/pages/home/index.vue Normal file
View File

View File

View File

@@ -1,21 +1,20 @@
import tailwindcss from "@tailwindcss/vite";
import vue from "@vitejs/plugin-vue";
import { fileURLToPath, URL } from "node:url";
import viteLegacy from "@vitejs/plugin-legacy";
import viteVue from "@vitejs/plugin-vue";
import viteVueJsx from "@vitejs/plugin-vue-jsx";
import path from "path";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [vue(), tailwindcss()],
plugins: [viteVue(), viteLegacy(), viteVueJsx(), tailwindcss()],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
"@": path.join(__dirname, "./src"),
},
},
server: {
host: "0.0.0.0",
port: 5174,
},
preview: {
host: "0.0.0.0",
port: 4174,
open: false,
},
});

1306
yarn.lock

File diff suppressed because it is too large Load Diff