🎨 重构用户端前端为vue开发,完善基础类和角色相关接口
This commit is contained in:
648
docs/Vue重构方案.md
Normal file
648
docs/Vue重构方案.md
Normal file
@@ -0,0 +1,648 @@
|
||||
# web-app Vue 3 重构方案
|
||||
|
||||
## 📋 方案概述
|
||||
|
||||
将 web-app 从传统的 jQuery 项目重构为 Vue 3 现代化单页应用(SPA)。
|
||||
|
||||
## 🎯 重构目标
|
||||
|
||||
### 技术目标
|
||||
- ✅ 使用 Vue 3 + Vite + TypeScript
|
||||
- ✅ 组件化、模块化开发
|
||||
- ✅ 统一前后端技术栈(与管理后台一致)
|
||||
- ✅ 优化性能和用户体验
|
||||
- ✅ 便于维护和扩展
|
||||
|
||||
### 业务目标
|
||||
- ✅ 保留所有原有功能
|
||||
- ✅ 改善 UI/UX 设计
|
||||
- ✅ 适配移动端
|
||||
- ✅ 支持主题切换
|
||||
- ✅ 国际化支持
|
||||
|
||||
## 🏗️ 技术栈选择
|
||||
|
||||
### 核心技术
|
||||
```json
|
||||
{
|
||||
"框架": "Vue 3 (Composition API)",
|
||||
"构建工具": "Vite 5",
|
||||
"语言": "TypeScript",
|
||||
"状态管理": "Pinia",
|
||||
"路由": "Vue Router 4",
|
||||
"HTTP": "Axios",
|
||||
"WebSocket": "原生 WebSocket / Socket.io-client"
|
||||
}
|
||||
```
|
||||
|
||||
### UI 框架选择
|
||||
|
||||
#### 方案一:Element Plus(推荐)
|
||||
```bash
|
||||
# 优点
|
||||
✅ 与管理后台统一(web/ 使用的就是 Element Plus)
|
||||
✅ 组件丰富,开箱即用
|
||||
✅ 中文文档完善
|
||||
✅ 支持深色主题
|
||||
✅ TypeScript 支持好
|
||||
|
||||
# 缺点
|
||||
⚠️ 组件较重(如果只需要简单 UI,可能过度设计)
|
||||
```
|
||||
|
||||
#### 方案二:Naive UI
|
||||
```bash
|
||||
# 优点
|
||||
✅ 轻量级,性能好
|
||||
✅ TypeScript 原生支持
|
||||
✅ 组件设计现代
|
||||
✅ 支持深色主题
|
||||
|
||||
# 缺点
|
||||
⚠️ 与管理后台不统一
|
||||
```
|
||||
|
||||
#### 方案三:自定义 UI(参考 auth.html)
|
||||
```bash
|
||||
# 优点
|
||||
✅ 完全自定义,独特设计
|
||||
✅ 轻量级
|
||||
✅ 可以复用已有的 auth.css 样式
|
||||
|
||||
# 缺点
|
||||
⚠️ 需要自己实现所有组件
|
||||
⚠️ 开发时间较长
|
||||
```
|
||||
|
||||
**推荐:Element Plus + 自定义样式**
|
||||
- 使用 Element Plus 的基础组件(表单、按钮、对话框等)
|
||||
- 自定义主题和关键页面样式
|
||||
- 兼顾开发效率和设计独特性
|
||||
|
||||
## 📂 项目结构
|
||||
|
||||
### 新项目结构(推荐)
|
||||
|
||||
```
|
||||
web-app-vue/ # 新建的 Vue 项目
|
||||
├── public/
|
||||
│ ├── favicon.ico
|
||||
│ └── img/ # 静态图片资源
|
||||
├── src/
|
||||
│ ├── api/ # API 接口
|
||||
│ │ ├── auth.ts # 认证接口
|
||||
│ │ ├── character.ts # 角色接口
|
||||
│ │ ├── chat.ts # 对话接口
|
||||
│ │ └── index.ts # axios 配置
|
||||
│ ├── assets/ # 资源文件
|
||||
│ │ ├── styles/ # 全局样式
|
||||
│ │ │ ├── index.scss
|
||||
│ │ │ ├── variables.scss
|
||||
│ │ │ └── dark-theme.scss
|
||||
│ │ └── images/
|
||||
│ ├── components/ # 通用组件
|
||||
│ │ ├── common/ # 基础组件
|
||||
│ │ │ ├── AppHeader.vue
|
||||
│ │ │ ├── AppSidebar.vue
|
||||
│ │ │ └── Loading.vue
|
||||
│ │ ├── chat/ # 对话组件
|
||||
│ │ │ ├── ChatMessage.vue
|
||||
│ │ │ ├── ChatInput.vue
|
||||
│ │ │ ├── MessageSwipe.vue
|
||||
│ │ │ └── VoiceInput.vue
|
||||
│ │ ├── character/ # 角色组件
|
||||
│ │ │ ├── CharacterCard.vue
|
||||
│ │ │ ├── CharacterList.vue
|
||||
│ │ │ ├── CharacterEditor.vue
|
||||
│ │ │ └── CharacterImport.vue
|
||||
│ │ └── settings/ # 设置组件
|
||||
│ │ ├── AIConfig.vue
|
||||
│ │ ├── ThemeConfig.vue
|
||||
│ │ └── UserProfile.vue
|
||||
│ ├── composables/ # 组合式函数
|
||||
│ │ ├── useAuth.ts # 认证相关
|
||||
│ │ ├── useChat.ts # 对话相关
|
||||
│ │ ├── useCharacter.ts # 角色相关
|
||||
│ │ └── useWebSocket.ts # WebSocket
|
||||
│ ├── layouts/ # 布局组件
|
||||
│ │ ├── DefaultLayout.vue # 默认布局
|
||||
│ │ ├── AuthLayout.vue # 认证页布局
|
||||
│ │ └── ChatLayout.vue # 对话页布局
|
||||
│ ├── router/ # 路由配置
|
||||
│ │ ├── index.ts
|
||||
│ │ └── guards.ts # 路由守卫
|
||||
│ ├── stores/ # Pinia 状态管理
|
||||
│ │ ├── auth.ts # 用户认证
|
||||
│ │ ├── chat.ts # 对话状态
|
||||
│ │ ├── character.ts # 角色管理
|
||||
│ │ ├── settings.ts # 设置
|
||||
│ │ └── index.ts
|
||||
│ ├── types/ # TypeScript 类型
|
||||
│ │ ├── api.d.ts
|
||||
│ │ ├── character.d.ts
|
||||
│ │ ├── chat.d.ts
|
||||
│ │ └── user.d.ts
|
||||
│ ├── utils/ # 工具函数
|
||||
│ │ ├── request.ts # HTTP 请求封装
|
||||
│ │ ├── storage.ts # 本地存储
|
||||
│ │ ├── websocket.ts # WebSocket 封装
|
||||
│ │ ├── format.ts # 格式化
|
||||
│ │ └── validate.ts # 验证
|
||||
│ ├── views/ # 页面视图
|
||||
│ │ ├── auth/ # 认证页
|
||||
│ │ │ ├── Login.vue
|
||||
│ │ │ └── Register.vue
|
||||
│ │ ├── home/ # 主页
|
||||
│ │ │ └── Index.vue
|
||||
│ │ ├── character/ # 角色页
|
||||
│ │ │ ├── List.vue
|
||||
│ │ │ ├── Detail.vue
|
||||
│ │ │ └── Create.vue
|
||||
│ │ ├── chat/ # 对话页
|
||||
│ │ │ └── Index.vue
|
||||
│ │ ├── settings/ # 设置页
|
||||
│ │ │ └── Index.vue
|
||||
│ │ └── user/ # 用户中心
|
||||
│ │ └── Profile.vue
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.ts # 入口文件
|
||||
├── .env.development # 开发环境配置
|
||||
├── .env.production # 生产环境配置
|
||||
├── index.html
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
├── vite.config.ts
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### 与现有项目的关系
|
||||
|
||||
```
|
||||
st/
|
||||
├── server/ # Go 后端(不变)
|
||||
├── web/ # Vue 管理后台(不变)
|
||||
├── web-app/ # 旧项目(保留备份)
|
||||
└── web-app-vue/ # 新的 Vue 前台(新建)
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 🚀 实施步骤
|
||||
|
||||
### 阶段一:项目初始化(1-2 天)
|
||||
|
||||
#### 1.1 创建 Vue 项目
|
||||
```bash
|
||||
# 使用 Vite 创建项目
|
||||
npm create vite@latest web-app-vue -- --template vue-ts
|
||||
|
||||
cd web-app-vue
|
||||
npm install
|
||||
```
|
||||
|
||||
#### 1.2 安装依赖
|
||||
```bash
|
||||
# UI 框架
|
||||
npm install element-plus
|
||||
|
||||
# 路由和状态管理
|
||||
npm install vue-router@4 pinia
|
||||
|
||||
# HTTP 和工具
|
||||
npm install axios
|
||||
npm install @vueuse/core # Vue 常用组合式函数
|
||||
|
||||
# 开发依赖
|
||||
npm install -D sass
|
||||
npm install -D unplugin-vue-components unplugin-auto-import
|
||||
```
|
||||
|
||||
#### 1.3 配置 Vite
|
||||
```typescript
|
||||
// vite.config.ts
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
imports: ['vue', 'vue-router', 'pinia'],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/app': {
|
||||
target: 'http://localhost:8888',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
#### 1.4 基础配置
|
||||
```typescript
|
||||
// src/main.ts
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import router from './router'
|
||||
import App from './App.vue'
|
||||
import './assets/styles/index.scss'
|
||||
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
```
|
||||
|
||||
### 阶段二:核心功能开发(5-7 天)
|
||||
|
||||
#### 2.1 认证模块(1 天)
|
||||
- [x] 登录页面
|
||||
- [x] 注册页面
|
||||
- [x] Token 管理
|
||||
- [x] 路由守卫
|
||||
|
||||
#### 2.2 角色管理(2 天)
|
||||
- [ ] 角色列表
|
||||
- [ ] 角色详情
|
||||
- [ ] 角色创建/编辑
|
||||
- [ ] 角色导入/导出
|
||||
- [ ] 收藏功能
|
||||
|
||||
#### 2.3 对话功能(3 天)
|
||||
- [ ] 对话界面
|
||||
- [ ] 消息发送
|
||||
- [ ] 消息渲染
|
||||
- [ ] Swipe 功能
|
||||
- [ ] WebSocket 实时通信
|
||||
- [ ] 流式输出
|
||||
|
||||
#### 2.4 设置页面(1 天)
|
||||
- [ ] AI 配置
|
||||
- [ ] 主题设置
|
||||
- [ ] 用户偏好
|
||||
|
||||
### 阶段三:高级功能(3-5 天)
|
||||
|
||||
#### 3.1 AI 功能
|
||||
- [ ] 多模型支持
|
||||
- [ ] Prompt 管理
|
||||
- [ ] World Info
|
||||
- [ ] 向量记忆
|
||||
|
||||
#### 3.2 UI/UX 优化
|
||||
- [ ] 响应式布局
|
||||
- [ ] 移动端适配
|
||||
- [ ] 主题切换
|
||||
- [ ] 动画效果
|
||||
|
||||
#### 3.3 文件管理
|
||||
- [ ] 头像上传
|
||||
- [ ] 文件管理
|
||||
- [ ] 图片预览
|
||||
|
||||
### 阶段四:测试和优化(2-3 天)
|
||||
|
||||
- [ ] 单元测试
|
||||
- [ ] E2E 测试
|
||||
- [ ] 性能优化
|
||||
- [ ] 打包优化
|
||||
- [ ] 部署配置
|
||||
|
||||
## 📦 关键组件示例
|
||||
|
||||
### 1. 认证 Store
|
||||
|
||||
```typescript
|
||||
// src/stores/auth.ts
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { login, register, getUserInfo } from '@/api/auth'
|
||||
import type { LoginRequest, User } from '@/types/user'
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
const token = ref<string>(localStorage.getItem('st_access_token') || '')
|
||||
const user = ref<User | null>(null)
|
||||
|
||||
const isLoggedIn = computed(() => !!token.value)
|
||||
|
||||
async function handleLogin(data: LoginRequest) {
|
||||
const res = await login(data)
|
||||
token.value = res.data.token
|
||||
user.value = res.data.user
|
||||
localStorage.setItem('st_access_token', res.data.token)
|
||||
localStorage.setItem('st_user_info', JSON.stringify(res.data.user))
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
token.value = ''
|
||||
user.value = null
|
||||
localStorage.removeItem('st_access_token')
|
||||
localStorage.removeItem('st_user_info')
|
||||
}
|
||||
|
||||
async function fetchUserInfo() {
|
||||
const res = await getUserInfo()
|
||||
user.value = res.data
|
||||
}
|
||||
|
||||
return {
|
||||
token,
|
||||
user,
|
||||
isLoggedIn,
|
||||
handleLogin,
|
||||
handleLogout,
|
||||
fetchUserInfo,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 2. HTTP 请求封装
|
||||
|
||||
```typescript
|
||||
// src/utils/request.ts
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8888',
|
||||
timeout: 10000,
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
request.interceptors.request.use(
|
||||
(config) => {
|
||||
const authStore = useAuthStore()
|
||||
if (authStore.token) {
|
||||
config.headers['x-token'] = authStore.token
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
request.interceptors.response.use(
|
||||
(response) => {
|
||||
const res = response.data
|
||||
if (res.code !== 0) {
|
||||
ElMessage.error(res.msg || '请求失败')
|
||||
return Promise.reject(new Error(res.msg || '请求失败'))
|
||||
}
|
||||
return res
|
||||
},
|
||||
(error) => {
|
||||
ElMessage.error(error.message || '网络错误')
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default request
|
||||
```
|
||||
|
||||
### 3. 角色卡片组件
|
||||
|
||||
```vue
|
||||
<!-- src/components/character/CharacterCard.vue -->
|
||||
<template>
|
||||
<div class="character-card" @click="handleClick">
|
||||
<div class="card-header">
|
||||
<img :src="character.avatar" :alt="character.name" class="avatar" />
|
||||
<el-icon v-if="isFavorite" class="favorite-icon">
|
||||
<Star />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h3 class="character-name">{{ character.name }}</h3>
|
||||
<p class="character-description">{{ character.description }}</p>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<el-tag v-for="tag in character.tags" :key="tag" size="small">
|
||||
{{ tag }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import type { Character } from '@/types/character'
|
||||
|
||||
interface Props {
|
||||
character: Character
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<{
|
||||
click: [character: Character]
|
||||
}>()
|
||||
|
||||
const isFavorite = computed(() => props.character.isFavorite)
|
||||
|
||||
function handleClick() {
|
||||
emit('click', props.character)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.character-card {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
background: var(--el-bg-color);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
position: relative;
|
||||
|
||||
.avatar {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.favorite-icon {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
color: #f59e0b;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 16px;
|
||||
|
||||
.character-name {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.character-description {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: 0 16px 16px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 🔄 迁移策略
|
||||
|
||||
### 渐进式迁移(推荐)
|
||||
|
||||
1. **Phase 1: 新功能使用 Vue**
|
||||
- 保留旧 web-app,用于稳定运行
|
||||
- 新功能在 web-app-vue 开发
|
||||
- 通过路由切换新旧页面
|
||||
|
||||
2. **Phase 2: 逐步迁移**
|
||||
- 按模块迁移(认证 → 角色 → 对话 → 设置)
|
||||
- 每个模块迁移后充分测试
|
||||
- 确保功能完整
|
||||
|
||||
3. **Phase 3: 完全替换**
|
||||
- 所有功能迁移完成
|
||||
- 彻底删除旧代码
|
||||
- web-app-vue 改名为 web-app
|
||||
|
||||
### 一次性重写(不推荐,风险高)
|
||||
|
||||
除非你有充足的时间和人力,否则不建议一次性重写整个项目。
|
||||
|
||||
## 📊 与现有项目的集成
|
||||
|
||||
### Go 后端配置
|
||||
|
||||
```go
|
||||
// server/initialize/router.go
|
||||
|
||||
// 前台 Vue 应用静态文件
|
||||
webAppVuePath := "../web-app-vue/dist"
|
||||
if _, err := os.Stat(webAppVuePath); err == nil {
|
||||
Router.Static("/assets", webAppVuePath+"/assets")
|
||||
Router.StaticFile("/", webAppVuePath+"/index.html")
|
||||
global.GVA_LOG.Info("前台 Vue 应用已启动: " + webAppVuePath)
|
||||
}
|
||||
```
|
||||
|
||||
### 环境配置
|
||||
|
||||
```bash
|
||||
# .env.development
|
||||
VITE_API_BASE_URL=http://localhost:8888
|
||||
VITE_WS_URL=ws://localhost:8888
|
||||
|
||||
# .env.production
|
||||
VITE_API_BASE_URL=https://your-domain.com
|
||||
VITE_WS_URL=wss://your-domain.com
|
||||
```
|
||||
|
||||
## 🎯 开发优先级
|
||||
|
||||
### P0(必须)
|
||||
1. ✅ 用户认证(登录/注册)
|
||||
2. ✅ 角色列表和详情
|
||||
3. ✅ 基本对话功能
|
||||
4. ✅ AI 配置
|
||||
|
||||
### P1(重要)
|
||||
5. ⚪ 角色编辑和创建
|
||||
6. ⚪ 消息 Swipe
|
||||
7. ⚪ 文件上传
|
||||
8. ⚪ 设置页面
|
||||
|
||||
### P2(可选)
|
||||
9. ⚪ World Info
|
||||
10. ⚪ 向量记忆
|
||||
11. ⚪ 高级 AI 功能
|
||||
12. ⚪ 主题切换
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
### 1. 保持功能一致性
|
||||
- 确保所有原有功能都能在 Vue 版本中实现
|
||||
- 参考旧版的交互逻辑
|
||||
- 不要遗漏边界情况
|
||||
|
||||
### 2. 性能优化
|
||||
- 使用虚拟滚动(角色列表、消息列表)
|
||||
- 图片懒加载
|
||||
- 路由懒加载
|
||||
- 打包分析和优化
|
||||
|
||||
### 3. 移动端适配
|
||||
- 响应式设计
|
||||
- 触摸手势支持
|
||||
- 移动端专用布局
|
||||
|
||||
### 4. 国际化
|
||||
- 使用 vue-i18n
|
||||
- 支持多语言切换
|
||||
- 本地化日期和数字
|
||||
|
||||
## 📚 学习资源
|
||||
|
||||
- Vue 3 官方文档: https://cn.vuejs.org/
|
||||
- Vite 文档: https://cn.vitejs.dev/
|
||||
- Element Plus: https://element-plus.org/zh-CN/
|
||||
- Pinia 文档: https://pinia.vuejs.org/zh/
|
||||
- TypeScript 文档: https://www.typescriptlang.org/zh/
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
### 为什么要重构?
|
||||
|
||||
1. ✅ **代码质量**:从混乱的 jQuery 代码到清晰的组件化代码
|
||||
2. ✅ **开发效率**:热更新、TypeScript、自动导入
|
||||
3. ✅ **性能提升**:Vite 构建、按需加载、虚拟滚动
|
||||
4. ✅ **易于维护**:模块化、类型安全、单一职责
|
||||
5. ✅ **技术统一**:与管理后台使用相同技术栈
|
||||
6. ✅ **未来发展**:易于扩展新功能
|
||||
|
||||
### 建议的时间线
|
||||
|
||||
- **快速原型**(1 周):核心功能,可以开始使用
|
||||
- **MVP 版本**(2-3 周):主要功能完成
|
||||
- **完整版本**(4-6 周):所有功能迁移完成
|
||||
- **优化打磨**(持续):性能优化、bug 修复
|
||||
|
||||
---
|
||||
|
||||
**更新日期**: 2026-02-10
|
||||
**版本**: v1.0.0
|
||||
Reference in New Issue
Block a user