From b635c9e7d4400393b89728c9b06c2280392b9de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=9D=B0?= Date: Mon, 22 Sep 2025 15:12:38 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=E6=8F=90=E4=BA=A4=20?= =?UTF-8?q?-=20=E5=91=A8=E4=B8=80=202025/09/22=2015:12:38.91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Linux使用说明.md | 259 +++++++ build_frontend.bat | 54 ++ build_frontend.sh | 60 ++ download_nodejs.bat | 15 + frontend/README.md | 211 ++++++ frontend/index.html | 13 + frontend/package-lock.json | 35 + frontend/package.json | 33 + frontend/src/App.vue | 38 + frontend/src/components/ChatWidget.vue | 475 +++++++++++++ frontend/src/i18n/index.ts | 17 + frontend/src/i18n/locales/en.json | 252 +++++++ frontend/src/i18n/locales/zh.json | 252 +++++++ frontend/src/layout/index.vue | 304 ++++++++ frontend/src/main.ts | 27 + frontend/src/router/index.ts | 61 ++ frontend/src/stores/useAlertStore.ts | 286 ++++++++ frontend/src/stores/useAppStore.ts | 60 ++ frontend/src/stores/useChatStore.ts | 297 ++++++++ frontend/src/views/AlertRules.vue | 561 +++++++++++++++ frontend/src/views/Alerts.vue | 463 +++++++++++++ frontend/src/views/Chat.vue | 753 ++++++++++++++++++++ frontend/src/views/Dashboard.vue | 667 ++++++++++++++++++ frontend/src/views/FieldMapping.vue | 366 ++++++++++ frontend/src/views/Knowledge.vue | 330 +++++++++ frontend/src/views/System.vue | 409 +++++++++++ frontend/tsconfig.json | 25 + frontend/tsconfig.node.json | 10 + frontend/vite.config.ts | 43 ++ init_database.py | 8 +- install_dependencies.sh | 133 ++++ src/web/app_new.py | 925 ++++++------------------- src/web/templates/index_new.html | 133 ++++ start_frontend.bat | 45 ++ start_frontend.sh | 51 ++ start_frontend_portable.bat | 51 ++ start_legacy.bat | 22 + start_traditional.bat | 32 + start_traditional.sh | 50 ++ 前端架构优化总结.md | 265 +++++++ 灵活字段映射系统使用说明.md | 219 ------ 41 files changed, 7360 insertions(+), 950 deletions(-) create mode 100644 Linux使用说明.md create mode 100644 build_frontend.bat create mode 100644 build_frontend.sh create mode 100644 download_nodejs.bat create mode 100644 frontend/README.md create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/App.vue create mode 100644 frontend/src/components/ChatWidget.vue create mode 100644 frontend/src/i18n/index.ts create mode 100644 frontend/src/i18n/locales/en.json create mode 100644 frontend/src/i18n/locales/zh.json create mode 100644 frontend/src/layout/index.vue create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/router/index.ts create mode 100644 frontend/src/stores/useAlertStore.ts create mode 100644 frontend/src/stores/useAppStore.ts create mode 100644 frontend/src/stores/useChatStore.ts create mode 100644 frontend/src/views/AlertRules.vue create mode 100644 frontend/src/views/Alerts.vue create mode 100644 frontend/src/views/Chat.vue create mode 100644 frontend/src/views/Dashboard.vue create mode 100644 frontend/src/views/FieldMapping.vue create mode 100644 frontend/src/views/Knowledge.vue create mode 100644 frontend/src/views/System.vue create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts create mode 100644 install_dependencies.sh create mode 100644 src/web/templates/index_new.html create mode 100644 start_frontend.bat create mode 100644 start_frontend.sh create mode 100644 start_frontend_portable.bat create mode 100644 start_legacy.bat create mode 100644 start_traditional.bat create mode 100644 start_traditional.sh create mode 100644 前端架构优化总结.md delete mode 100644 灵活字段映射系统使用说明.md diff --git a/Linux使用说明.md b/Linux使用说明.md new file mode 100644 index 0000000..d26c7d4 --- /dev/null +++ b/Linux使用说明.md @@ -0,0 +1,259 @@ +# TSP智能助手 Linux 使用说明 + +## 🐧 Linux环境部署指南 + +### 1. 系统要求 + +- **操作系统**: Ubuntu 18.04+, CentOS 7+, Debian 9+, Arch Linux +- **Node.js**: 18.x 或更高版本 +- **Python**: 3.7 或更高版本 +- **内存**: 至少 2GB RAM +- **磁盘**: 至少 1GB 可用空间 + +### 2. 快速开始 + +#### 方法一:一键安装(推荐) + +```bash +# 下载并运行安装脚本 +chmod +x install_dependencies.sh +./install_dependencies.sh +``` + +#### 方法二:手动安装 + +```bash +# 安装Node.js (Ubuntu/Debian) +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt-get install -y nodejs + +# 安装Node.js (CentOS/RHEL) +curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - +sudo yum install -y nodejs + +# 安装Python依赖 +pip3 install -r requirements.txt + +# 安装前端依赖 +cd frontend +npm install +cd .. +``` + +### 3. 启动服务 + +#### 启动传统版本(立即可用) + +```bash +chmod +x start_traditional.sh +./start_traditional.sh +``` + +访问:http://localhost:5000 + +#### 启动现代化前端(需要Node.js) + +```bash +chmod +x start_frontend.sh +./start_frontend.sh +``` + +访问:http://localhost:3000 + +#### 构建生产版本 + +```bash +chmod +x build_frontend.sh +./build_frontend.sh +``` + +### 4. 功能对比 + +| 功能 | 传统版本 | 现代化前端 | +|------|----------|------------| +| 基础功能 | ✅ 完整支持 | ✅ 完整支持 | +| 聊天系统 | ✅ 支持 | ✅ 统一组件 | +| 预警管理 | ✅ 支持 | ✅ 增强体验 | +| 国际化 | ❌ 仅中文 | ✅ 中英文切换 | +| 主题切换 | ❌ 固定主题 | ✅ 暗色/亮色 | +| 响应式设计 | ⚠️ 基础支持 | ✅ 完整支持 | +| 开发体验 | ⚠️ 传统开发 | ✅ 现代化开发 | + +### 5. 常见问题 + +#### Q: Node.js安装失败 +```bash +# 检查Node.js版本 +node --version +npm --version + +# 如果版本过低,重新安装 +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +sudo apt-get install -y nodejs +``` + +#### Q: Python依赖安装失败 +```bash +# 升级pip +pip3 install --upgrade pip + +# 重新安装依赖 +pip3 install -r requirements.txt +``` + +#### Q: 前端依赖安装失败 +```bash +# 清理缓存 +npm cache clean --force + +# 删除node_modules重新安装 +rm -rf frontend/node_modules +cd frontend +npm install +``` + +#### Q: 端口被占用 +```bash +# 查看端口占用 +sudo netstat -tlnp | grep :5000 +sudo netstat -tlnp | grep :3000 + +# 杀死占用进程 +sudo kill -9 +``` + +### 6. 开发环境配置 + +#### 使用VS Code开发 + +```bash +# 安装VS Code +wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg +sudo install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/ +sudo sh -c 'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/trusted.gpg.d/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list' +sudo apt update +sudo apt install code + +# 安装推荐扩展 +code --install-extension ms-python.python +code --install-extension vue.volar +code --install-extension bradlc.vscode-tailwindcss +``` + +#### 使用Docker开发 + +```bash +# 创建Dockerfile +cat > Dockerfile << EOF +FROM node:20-alpine +WORKDIR /app +COPY frontend/package*.json ./ +RUN npm install +COPY frontend/ . +EXPOSE 3000 +CMD ["npm", "run", "dev"] +EOF + +# 构建并运行 +docker build -t tsp-frontend . +docker run -p 3000:3000 tsp-frontend +``` + +### 7. 生产部署 + +#### 使用Nginx反向代理 + +```bash +# 安装Nginx +sudo apt install nginx + +# 配置Nginx +sudo tee /etc/nginx/sites-available/tsp-assistant << EOF +server { + listen 80; + server_name your-domain.com; + + location / { + proxy_pass http://localhost:3000; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location /api/ { + proxy_pass http://localhost:5000; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } +} +EOF + +# 启用站点 +sudo ln -s /etc/nginx/sites-available/tsp-assistant /etc/nginx/sites-enabled/ +sudo nginx -t +sudo systemctl reload nginx +``` + +#### 使用PM2进程管理 + +```bash +# 安装PM2 +npm install -g pm2 + +# 启动应用 +pm2 start src/web/app.py --name "tsp-backend" --interpreter python3 +pm2 start frontend/package.json --name "tsp-frontend" + +# 保存配置 +pm2 save +pm2 startup +``` + +### 8. 监控和日志 + +```bash +# 查看应用状态 +pm2 status + +# 查看日志 +pm2 logs tsp-backend +pm2 logs tsp-frontend + +# 重启应用 +pm2 restart tsp-backend +pm2 restart tsp-frontend +``` + +### 9. 备份和恢复 + +```bash +# 备份数据库 +cp tsp_assistant.db tsp_assistant_backup_$(date +%Y%m%d).db + +# 备份配置文件 +tar -czf config_backup_$(date +%Y%m%d).tar.gz config/ + +# 恢复数据库 +cp tsp_assistant_backup_20240101.db tsp_assistant.db +``` + +--- + +## 🚀 快速命令参考 + +```bash +# 一键启动传统版本 +./start_traditional.sh + +# 一键启动现代化前端 +./start_frontend.sh + +# 构建生产版本 +./build_frontend.sh + +# 安装所有依赖 +./install_dependencies.sh +``` + +现在您可以在Linux环境中愉快地使用TSP智能助手了!🎉 diff --git a/build_frontend.bat b/build_frontend.bat new file mode 100644 index 0000000..e6c4be2 --- /dev/null +++ b/build_frontend.bat @@ -0,0 +1,54 @@ +@echo off +echo 构建TSP智能助手前端... +echo. + +cd frontend + +echo 检查Node.js环境... +node --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo 错误: 未找到Node.js,请先安装Node.js + echo 下载地址: https://nodejs.org/ + pause + exit /b 1 +) + +echo 检查npm环境... +npm --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo 错误: 未找到npm,请检查Node.js安装 + pause + exit /b 1 +) + +echo 检查依赖包... +if not exist "node_modules" ( + echo 安装依赖包... + npm install + if %errorlevel% neq 0 ( + echo 错误: 依赖包安装失败 + pause + exit /b 1 + ) +) + +echo 运行类型检查... +npm run type-check +if %errorlevel% neq 0 ( + echo 警告: 类型检查失败,但继续构建... +) + +echo 构建生产版本... +npm run build +if %errorlevel% neq 0 ( + echo 错误: 构建失败 + pause + exit /b 1 +) + +echo. +echo 构建完成! +echo 构建文件已输出到: src/web/static/dist +echo. + +pause diff --git a/build_frontend.sh b/build_frontend.sh new file mode 100644 index 0000000..5e15487 --- /dev/null +++ b/build_frontend.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +echo "构建TSP智能助手前端..." +echo + +# 检查Node.js环境 +echo "检查Node.js环境..." +if ! command -v node &> /dev/null; then + echo "错误: 未找到Node.js,请先安装Node.js" + echo "安装命令:" + echo " Ubuntu/Debian: sudo apt update && sudo apt install nodejs npm" + echo " CentOS/RHEL: sudo yum install nodejs npm" + echo " 或访问: https://nodejs.org/" + exit 1 +fi + +# 检查npm环境 +echo "检查npm环境..." +if ! command -v npm &> /dev/null; then + echo "错误: 未找到npm,请检查Node.js安装" + exit 1 +fi + +echo "Node.js版本: $(node --version)" +echo "npm版本: $(npm --version)" +echo + +# 进入前端目录 +cd frontend + +# 检查依赖包 +echo "检查依赖包..." +if [ ! -d "node_modules" ]; then + echo "安装依赖包..." + npm install + if [ $? -ne 0 ]; then + echo "错误: 依赖包安装失败" + exit 1 + fi +fi + +# 运行类型检查 +echo "运行类型检查..." +npm run type-check +if [ $? -ne 0 ]; then + echo "警告: 类型检查失败,但继续构建..." +fi + +# 构建生产版本 +echo "构建生产版本..." +npm run build +if [ $? -ne 0 ]; then + echo "错误: 构建失败" + exit 1 +fi + +echo +echo "构建完成!" +echo "构建文件已输出到: ../src/web/static/dist" +echo diff --git a/download_nodejs.bat b/download_nodejs.bat new file mode 100644 index 0000000..3dd5464 --- /dev/null +++ b/download_nodejs.bat @@ -0,0 +1,15 @@ +@echo off +echo 下载便携版Node.js... +echo. + +echo 正在下载Node.js便携版... +echo 请手动下载Node.js便携版: +echo 1. 访问: https://nodejs.org/dist/v20.10.0/node-v20.10.0-win-x64.zip +echo 2. 下载并解压到 frontend\nodejs\ 目录 +echo 3. 确保解压后的目录结构为: frontend\nodejs\node.exe +echo. + +echo 下载完成后,请运行: .\start_frontend_portable.bat +echo. + +pause diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..4b7588a --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,211 @@ +# TSP智能助手前端 + +基于 Vue 3 + TypeScript + Element Plus 的现代化前端应用。 + +## 技术栈 + +- **Vue 3** - 渐进式 JavaScript 框架 +- **TypeScript** - JavaScript 的超集,提供类型安全 +- **Element Plus** - Vue 3 组件库 +- **Vite** - 快速构建工具 +- **Vue Router** - 官方路由管理器 +- **Pinia** - 状态管理库 +- **Vue I18n** - 国际化解决方案 +- **Socket.IO** - 实时通信 +- **ECharts** - 数据可视化 + +## 项目结构 + +``` +frontend/ +├── src/ +│ ├── components/ # 公共组件 +│ │ └── ChatWidget.vue # 聊天组件 +│ ├── views/ # 页面组件 +│ │ ├── Dashboard.vue # 仪表板 +│ │ ├── Chat.vue # 聊天页面 +│ │ ├── Alerts.vue # 预警管理 +│ │ ├── AlertRules.vue # 预警规则 +│ │ ├── Knowledge.vue # 知识库 +│ │ ├── FieldMapping.vue # 字段映射 +│ │ └── System.vue # 系统设置 +│ ├── stores/ # 状态管理 +│ │ ├── useAppStore.ts # 应用状态 +│ │ ├── useChatStore.ts # 聊天状态 +│ │ └── useAlertStore.ts # 预警状态 +│ ├── router/ # 路由配置 +│ ├── i18n/ # 国际化 +│ ├── App.vue # 根组件 +│ └── main.ts # 入口文件 +├── package.json +├── vite.config.ts +├── tsconfig.json +└── README.md +``` + +## 功能特性 + +### 🎯 核心功能 +- **统一聊天系统** - 支持首页和独立页面的聊天功能 +- **预警管理** - 实时预警监控和规则管理 +- **知识库管理** - 智能知识库的增删改查 +- **字段映射** - 灵活的字段映射配置 +- **系统监控** - 系统状态和性能监控 + +### 🌍 国际化支持 +- 支持中文/英文切换 +- 完整的国际化文本覆盖 +- Element Plus 组件国际化 + +### 🎨 现代化UI +- 响应式设计,支持移动端 +- 暗色/亮色主题切换 +- 优雅的动画效果 +- 统一的视觉风格 + +### 🔧 开发体验 +- TypeScript 类型安全 +- 组件自动导入 +- 热重载开发 +- ESLint 代码规范 + +## 快速开始 + +### 安装依赖 + +```bash +cd frontend +npm install +``` + +### 开发模式 + +```bash +npm run dev +``` + +访问 http://localhost:3000 + +### 构建生产版本 + +```bash +npm run build +``` + +构建文件将输出到 `../src/web/static/dist` + +### 类型检查 + +```bash +npm run type-check +``` + +## 开发指南 + +### 添加新页面 + +1. 在 `src/views/` 创建新的 Vue 组件 +2. 在 `src/router/index.ts` 添加路由配置 +3. 在 `src/i18n/locales/` 添加国际化文本 +4. 在 `src/layout/index.vue` 添加导航菜单 + +### 添加新组件 + +1. 在 `src/components/` 创建组件 +2. 使用 TypeScript 定义 props 和 emits +3. 添加必要的样式 + +### 状态管理 + +使用 Pinia 进行状态管理: + +```typescript +// stores/useExampleStore.ts +import { defineStore } from 'pinia' + +export const useExampleStore = defineStore('example', () => { + const state = ref('') + + const action = () => { + // 状态更新逻辑 + } + + return { state, action } +}) +``` + +### 国际化 + +在组件中使用: + +```vue + + + +``` + +## API 集成 + +### HTTP 请求 + +使用 axios 进行 API 调用: + +```typescript +import axios from 'axios' + +const response = await axios.get('/api/alerts') +``` + +### WebSocket 连接 + +使用 Socket.IO 进行实时通信: + +```typescript +import { io } from 'socket.io-client' + +const socket = io('ws://localhost:8765') +socket.on('message', (data) => { + // 处理消息 +}) +``` + +## 部署说明 + +### 开发环境 + +前端开发服务器运行在端口 3000,通过代理访问后端 API: + +- API 请求代理到 `http://localhost:5000` +- WebSocket 代理到 `ws://localhost:8765` + +### 生产环境 + +1. 运行 `npm run build` 构建生产版本 +2. 构建文件输出到 `../src/web/static/dist` +3. 后端 Flask 应用会直接提供静态文件服务 + +## 浏览器支持 + +- Chrome >= 87 +- Firefox >= 78 +- Safari >= 14 +- Edge >= 88 + +## 贡献指南 + +1. Fork 项目 +2. 创建功能分支 +3. 提交更改 +4. 推送到分支 +5. 创建 Pull Request + +## 许可证 + +MIT License diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..2ee14ec --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + TSP智能助手 + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..28d1ca2 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,35 @@ +{ + "name": "tsp-assistant-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tsp-assistant-frontend", + "version": "1.0.0", + "type": "module", + "dependencies": { + "@element-plus/icons-vue": "^2.1.0", + "@vitejs/plugin-vue": "^4.2.3", + "axios": "^1.4.0", + "echarts": "^5.4.2", + "element-plus": "^2.3.8", + "pinia": "^2.1.6", + "sass": "^1.64.1", + "socket.io-client": "^4.7.2", + "typescript": "^5.0.2", + "unplugin-auto-import": "^0.16.6", + "unplugin-vue-components": "^0.25.1", + "vite": "^4.4.5", + "vue": "^3.3.4", + "vue-echarts": "^6.6.0", + "vue-i18n": "^9.2.2", + "vue-router": "^4.2.4", + "vue-tsc": "^1.8.5" + }, + "devDependencies": { + "@types/node": "^20.4.5" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..f925813 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,33 @@ +{ + "name": "tsp-assistant-frontend", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "preview": "vite preview", + "type-check": "vue-tsc --noEmit" + }, + "dependencies": { + "vue": "^3.3.4", + "vue-router": "^4.2.4", + "pinia": "^2.1.6", + "element-plus": "^2.3.8", + "@element-plus/icons-vue": "^2.1.0", + "axios": "^1.4.0", + "vue-i18n": "^9.2.2", + "socket.io-client": "^4.7.2", + "echarts": "^5.4.2", + "vue-echarts": "^6.6.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^4.2.3", + "typescript": "^5.0.2", + "vue-tsc": "^1.8.5", + "vite": "^4.4.5", + "@types/node": "^20.4.5", + "sass": "^1.64.1", + "unplugin-auto-import": "^0.16.6", + "unplugin-vue-components": "^0.25.1" + } +} diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..8bbfad2 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/frontend/src/components/ChatWidget.vue b/frontend/src/components/ChatWidget.vue new file mode 100644 index 0000000..3225a58 --- /dev/null +++ b/frontend/src/components/ChatWidget.vue @@ -0,0 +1,475 @@ + + + + + diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts new file mode 100644 index 0000000..a6e3790 --- /dev/null +++ b/frontend/src/i18n/index.ts @@ -0,0 +1,17 @@ +import { createI18n } from 'vue-i18n' +import zh from './locales/zh.json' +import en from './locales/en.json' + +export function setupI18n() { + const i18n = createI18n({ + legacy: false, + locale: 'zh', + fallbackLocale: 'en', + messages: { + zh, + en + } + }) + + return i18n +} diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json new file mode 100644 index 0000000..b1ca37e --- /dev/null +++ b/frontend/src/i18n/locales/en.json @@ -0,0 +1,252 @@ +{ + "common": { + "confirm": "Confirm", + "cancel": "Cancel", + "save": "Save", + "delete": "Delete", + "edit": "Edit", + "add": "Add", + "search": "Search", + "refresh": "Refresh", + "loading": "Loading...", + "success": "Success", + "error": "Error", + "warning": "Warning", + "info": "Info", + "yes": "Yes", + "no": "No", + "close": "Close", + "submit": "Submit", + "reset": "Reset", + "back": "Back", + "next": "Next", + "previous": "Previous", + "finish": "Finish", + "start": "Start", + "stop": "Stop", + "pause": "Pause", + "resume": "Resume", + "enable": "Enable", + "disable": "Disable", + "status": "Status", + "time": "Time", + "name": "Name", + "description": "Description", + "type": "Type", + "level": "Level", + "priority": "Priority", + "category": "Category", + "action": "Action", + "result": "Result", + "message": "Message", + "details": "Details", + "settings": "Settings", + "config": "Configuration", + "version": "Version", + "author": "Author", + "created": "Created", + "updated": "Updated" + }, + "dashboard": { + "title": "Dashboard", + "overview": "Overview", + "systemHealth": "System Health", + "activeAlerts": "Active Alerts", + "recentActivity": "Recent Activity", + "quickActions": "Quick Actions", + "monitoring": "Monitoring", + "startMonitoring": "Start Monitoring", + "stopMonitoring": "Stop Monitoring", + "checkAlerts": "Check Alerts", + "healthScore": "Health Score", + "status": "Status", + "excellent": "Excellent", + "good": "Good", + "fair": "Fair", + "poor": "Poor", + "critical": "Critical", + "unknown": "Unknown" + }, + "chat": { + "title": "Intelligent Chat", + "startChat": "Start Chat", + "endChat": "End Chat", + "sendMessage": "Send Message", + "inputPlaceholder": "Please enter your question...", + "userId": "User ID", + "workOrderId": "Work Order ID", + "workOrderIdPlaceholder": "Leave empty to auto-create", + "createWorkOrder": "Create Work Order", + "quickActions": "Quick Actions", + "sessionInfo": "Session Info", + "connectionStatus": "Connection Status", + "connected": "Connected", + "disconnected": "Disconnected", + "typing": "Assistant is thinking...", + "welcome": "Welcome to TSP Intelligent Assistant", + "welcomeDesc": "Please click 'Start Chat' button to begin chatting", + "chatStarted": "Chat started, please describe your problem.", + "chatEnded": "Chat ended.", + "workOrderCreated": "Work order created successfully! Order ID: {orderId}", + "quickActions": { + "remoteStart": "Remote Start Issue", + "appDisplay": "APP Display Issue", + "bluetoothAuth": "Bluetooth Auth Issue", + "unbindVehicle": "Unbind Vehicle" + }, + "workOrder": { + "title": "Create Work Order", + "titleLabel": "Work Order Title", + "descriptionLabel": "Problem Description", + "categoryLabel": "Problem Category", + "priorityLabel": "Priority", + "categories": { + "technical": "Technical Issue", + "app": "APP Function", + "remoteControl": "Remote Control", + "vehicleBinding": "Vehicle Binding", + "other": "Other" + }, + "priorities": { + "low": "Low", + "medium": "Medium", + "high": "High", + "urgent": "Urgent" + } + } + }, + "alerts": { + "title": "Alert Management", + "rules": { + "title": "Alert Rules", + "addRule": "Add Rule", + "editRule": "Edit Rule", + "deleteRule": "Delete Rule", + "ruleName": "Rule Name", + "ruleType": "Alert Type", + "ruleLevel": "Alert Level", + "threshold": "Threshold", + "condition": "Condition Expression", + "checkInterval": "Check Interval (seconds)", + "cooldown": "Cooldown (seconds)", + "enabled": "Enable Rule", + "types": { + "performance": "Performance Alert", + "quality": "Quality Alert", + "volume": "Volume Alert", + "system": "System Alert", + "business": "Business Alert" + }, + "levels": { + "info": "Info", + "warning": "Warning", + "error": "Error", + "critical": "Critical" + }, + "presetTemplates": "Preset Templates", + "presetCategories": { + "performance": "Performance Alert Templates", + "business": "Business Alert Templates", + "system": "System Alert Templates", + "quality": "Quality Alert Templates" + } + }, + "statistics": { + "critical": "Critical Alerts", + "warning": "Warning Alerts", + "info": "Info Alerts", + "total": "Total Alerts" + }, + "filters": { + "all": "All Alerts", + "critical": "Critical", + "error": "Error", + "warning": "Warning", + "info": "Info" + }, + "sort": { + "timeDesc": "Time Descending", + "timeAsc": "Time Ascending", + "levelDesc": "Level Descending", + "levelAsc": "Level Ascending" + }, + "actions": { + "resolve": "Resolve", + "refresh": "Refresh" + }, + "empty": { + "title": "No Active Alerts", + "description": "System is running normally, no alerts to handle" + } + }, + "knowledge": { + "title": "Knowledge Management", + "add": "Add Knowledge", + "edit": "Edit Knowledge", + "delete": "Delete Knowledge", + "search": "Search Knowledge", + "category": "Category", + "title": "Title", + "content": "Content", + "tags": "Tags", + "status": "Status", + "actions": "Actions" + }, + "fieldMapping": { + "title": "Field Mapping", + "sourceField": "Source Field", + "targetField": "Target Field", + "mappingType": "Mapping Type", + "transformation": "Transformation Rule", + "addMapping": "Add Mapping", + "editMapping": "Edit Mapping", + "deleteMapping": "Delete Mapping", + "testMapping": "Test Mapping", + "importMapping": "Import Mapping", + "exportMapping": "Export Mapping" + }, + "system": { + "title": "System Settings", + "general": "General Settings", + "monitoring": "Monitoring Settings", + "alerts": "Alert Settings", + "integrations": "Integration Settings", + "backup": "Backup Settings", + "logs": "Log Management", + "performance": "Performance Monitoring", + "users": "User Management", + "permissions": "Permission Management" + }, + "navigation": { + "dashboard": "Dashboard", + "chat": "Intelligent Chat", + "alerts": "Alert Management", + "knowledge": "Knowledge Base", + "fieldMapping": "Field Mapping", + "system": "System Settings", + "logout": "Logout" + }, + "notifications": { + "monitoringStarted": "Monitoring service started", + "monitoringStopped": "Monitoring service stopped", + "alertsChecked": "Check completed, found {count} alerts", + "alertResolved": "Alert resolved", + "ruleCreated": "Rule created successfully", + "ruleUpdated": "Rule updated successfully", + "ruleDeleted": "Rule deleted successfully", + "workOrderCreated": "Work order created successfully", + "error": { + "startMonitoring": "Failed to start monitoring", + "stopMonitoring": "Failed to stop monitoring", + "checkAlerts": "Failed to check alerts", + "resolveAlert": "Failed to resolve alert", + "createRule": "Failed to create rule", + "updateRule": "Failed to update rule", + "deleteRule": "Failed to delete rule", + "createWorkOrder": "Failed to create work order", + "websocketConnection": "WebSocket connection failed, please check if server is running", + "requestTimeout": "Request timeout", + "networkError": "Network error" + } + } +} diff --git a/frontend/src/i18n/locales/zh.json b/frontend/src/i18n/locales/zh.json new file mode 100644 index 0000000..625af49 --- /dev/null +++ b/frontend/src/i18n/locales/zh.json @@ -0,0 +1,252 @@ +{ + "common": { + "confirm": "确认", + "cancel": "取消", + "save": "保存", + "delete": "删除", + "edit": "编辑", + "add": "添加", + "search": "搜索", + "refresh": "刷新", + "loading": "加载中...", + "success": "成功", + "error": "错误", + "warning": "警告", + "info": "信息", + "yes": "是", + "no": "否", + "close": "关闭", + "submit": "提交", + "reset": "重置", + "back": "返回", + "next": "下一步", + "previous": "上一步", + "finish": "完成", + "start": "开始", + "stop": "停止", + "pause": "暂停", + "resume": "继续", + "enable": "启用", + "disable": "禁用", + "status": "状态", + "time": "时间", + "name": "名称", + "description": "描述", + "type": "类型", + "level": "级别", + "priority": "优先级", + "category": "分类", + "action": "操作", + "result": "结果", + "message": "消息", + "details": "详情", + "settings": "设置", + "config": "配置", + "version": "版本", + "author": "作者", + "created": "创建时间", + "updated": "更新时间" + }, + "dashboard": { + "title": "仪表板", + "overview": "概览", + "systemHealth": "系统健康", + "activeAlerts": "活跃预警", + "recentActivity": "最近活动", + "quickActions": "快速操作", + "monitoring": "监控", + "startMonitoring": "启动监控", + "stopMonitoring": "停止监控", + "checkAlerts": "检查预警", + "healthScore": "健康评分", + "status": "状态", + "excellent": "优秀", + "good": "良好", + "fair": "一般", + "poor": "较差", + "critical": "严重", + "unknown": "未知" + }, + "chat": { + "title": "智能对话", + "startChat": "开始对话", + "endChat": "结束对话", + "sendMessage": "发送消息", + "inputPlaceholder": "请输入您的问题...", + "userId": "用户ID", + "workOrderId": "工单ID", + "workOrderIdPlaceholder": "留空则自动创建", + "createWorkOrder": "创建工单", + "quickActions": "快速操作", + "sessionInfo": "会话信息", + "connectionStatus": "连接状态", + "connected": "已连接", + "disconnected": "未连接", + "typing": "助手正在思考中...", + "welcome": "欢迎使用TSP智能助手", + "welcomeDesc": "请点击\"开始对话\"按钮开始聊天", + "chatStarted": "对话已开始,请描述您的问题。", + "chatEnded": "对话已结束。", + "workOrderCreated": "工单创建成功!工单号: {orderId}", + "quickActions": { + "remoteStart": "远程启动问题", + "appDisplay": "APP显示问题", + "bluetoothAuth": "蓝牙授权问题", + "unbindVehicle": "解绑车辆" + }, + "workOrder": { + "title": "创建工单", + "titleLabel": "工单标题", + "descriptionLabel": "问题描述", + "categoryLabel": "问题分类", + "priorityLabel": "优先级", + "categories": { + "technical": "技术问题", + "app": "APP功能", + "remoteControl": "远程控制", + "vehicleBinding": "车辆绑定", + "other": "其他" + }, + "priorities": { + "low": "低", + "medium": "中", + "high": "高", + "urgent": "紧急" + } + } + }, + "alerts": { + "title": "预警管理", + "rules": { + "title": "预警规则", + "addRule": "添加规则", + "editRule": "编辑规则", + "deleteRule": "删除规则", + "ruleName": "规则名称", + "ruleType": "预警类型", + "ruleLevel": "预警级别", + "threshold": "阈值", + "condition": "条件表达式", + "checkInterval": "检查间隔(秒)", + "cooldown": "冷却时间(秒)", + "enabled": "启用规则", + "types": { + "performance": "性能预警", + "quality": "质量预警", + "volume": "量级预警", + "system": "系统预警", + "business": "业务预警" + }, + "levels": { + "info": "信息", + "warning": "警告", + "error": "错误", + "critical": "严重" + }, + "presetTemplates": "预设模板", + "presetCategories": { + "performance": "性能预警模板", + "business": "业务预警模板", + "system": "系统预警模板", + "quality": "质量预警模板" + } + }, + "statistics": { + "critical": "严重预警", + "warning": "警告预警", + "info": "信息预警", + "total": "总预警数" + }, + "filters": { + "all": "全部预警", + "critical": "严重", + "error": "错误", + "warning": "警告", + "info": "信息" + }, + "sort": { + "timeDesc": "时间降序", + "timeAsc": "时间升序", + "levelDesc": "级别降序", + "levelAsc": "级别升序" + }, + "actions": { + "resolve": "解决", + "refresh": "刷新" + }, + "empty": { + "title": "暂无活跃预警", + "description": "系统运行正常,没有需要处理的预警" + } + }, + "knowledge": { + "title": "知识库管理", + "add": "添加知识", + "edit": "编辑知识", + "delete": "删除知识", + "search": "搜索知识", + "category": "分类", + "title": "标题", + "content": "内容", + "tags": "标签", + "status": "状态", + "actions": "操作" + }, + "fieldMapping": { + "title": "字段映射", + "sourceField": "源字段", + "targetField": "目标字段", + "mappingType": "映射类型", + "transformation": "转换规则", + "addMapping": "添加映射", + "editMapping": "编辑映射", + "deleteMapping": "删除映射", + "testMapping": "测试映射", + "importMapping": "导入映射", + "exportMapping": "导出映射" + }, + "system": { + "title": "系统设置", + "general": "常规设置", + "monitoring": "监控设置", + "alerts": "预警设置", + "integrations": "集成设置", + "backup": "备份设置", + "logs": "日志管理", + "performance": "性能监控", + "users": "用户管理", + "permissions": "权限管理" + }, + "navigation": { + "dashboard": "仪表板", + "chat": "智能对话", + "alerts": "预警管理", + "knowledge": "知识库", + "fieldMapping": "字段映射", + "system": "系统设置", + "logout": "退出登录" + }, + "notifications": { + "monitoringStarted": "监控服务已启动", + "monitoringStopped": "监控服务已停止", + "alertsChecked": "检查完成,发现 {count} 个预警", + "alertResolved": "预警已解决", + "ruleCreated": "规则创建成功", + "ruleUpdated": "规则更新成功", + "ruleDeleted": "规则删除成功", + "workOrderCreated": "工单创建成功", + "error": { + "startMonitoring": "启动监控失败", + "stopMonitoring": "停止监控失败", + "checkAlerts": "检查预警失败", + "resolveAlert": "解决预警失败", + "createRule": "创建规则失败", + "updateRule": "更新规则失败", + "deleteRule": "删除规则失败", + "createWorkOrder": "创建工单失败", + "websocketConnection": "WebSocket连接失败,请检查服务器是否启动", + "requestTimeout": "请求超时", + "networkError": "网络错误" + } + } +} diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue new file mode 100644 index 0000000..5be4cd5 --- /dev/null +++ b/frontend/src/layout/index.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..bdfacef --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,27 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import 'element-plus/theme-chalk/dark/css-vars.css' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import zhCn from 'element-plus/dist/locale/zh-cn.mjs' + +import App from './App.vue' +import router from './router' +import { setupI18n } from './i18n' + +const app = createApp(App) + +// 注册Element Plus图标 +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +app.use(createPinia()) +app.use(router) +app.use(ElementPlus, { + locale: zhCn, +}) +app.use(setupI18n()) + +app.mount('#app') diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts new file mode 100644 index 0000000..37afe6d --- /dev/null +++ b/frontend/src/router/index.ts @@ -0,0 +1,61 @@ +import { createRouter, createWebHistory } from 'vue-router' +import type { RouteRecordRaw } from 'vue-router' + +const routes: RouteRecordRaw[] = [ + { + path: '/', + name: 'Layout', + component: () => import('@/layout/index.vue'), + children: [ + { + path: '', + name: 'Dashboard', + component: () => import('@/views/Dashboard.vue'), + meta: { title: 'dashboard.title' } + }, + { + path: '/chat', + name: 'Chat', + component: () => import('@/views/Chat.vue'), + meta: { title: 'chat.title' } + }, + { + path: '/alerts', + name: 'Alerts', + component: () => import('@/views/Alerts.vue'), + meta: { title: 'alerts.title' } + }, + { + path: '/alerts/rules', + name: 'AlertRules', + component: () => import('@/views/AlertRules.vue'), + meta: { title: 'alerts.rules.title' } + }, + { + path: '/knowledge', + name: 'Knowledge', + component: () => import('@/views/Knowledge.vue'), + meta: { title: 'knowledge.title' } + }, + { + path: '/field-mapping', + name: 'FieldMapping', + component: () => import('@/views/FieldMapping.vue'), + meta: { title: 'fieldMapping.title' } + }, + { + path: '/system', + name: 'System', + component: () => import('@/views/System.vue'), + meta: { title: 'system.title' } + } + ] + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router diff --git a/frontend/src/stores/useAlertStore.ts b/frontend/src/stores/useAlertStore.ts new file mode 100644 index 0000000..8d5eb97 --- /dev/null +++ b/frontend/src/stores/useAlertStore.ts @@ -0,0 +1,286 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import axios from 'axios' + +export interface Alert { + id: number + rule_name: string + message: string + level: 'critical' | 'error' | 'warning' | 'info' + alert_type: 'performance' | 'quality' | 'volume' | 'system' | 'business' + data?: any + created_at: string + resolved_at?: string + status: 'active' | 'resolved' +} + +export interface AlertRule { + name: string + description?: string + alert_type: 'performance' | 'quality' | 'volume' | 'system' | 'business' + level: 'critical' | 'error' | 'warning' | 'info' + threshold: number + condition: string + enabled: boolean + check_interval: number + cooldown: number +} + +export interface SystemHealth { + health_score: number + status: 'excellent' | 'good' | 'fair' | 'poor' | 'critical' | 'unknown' + details?: any +} + +export interface MonitorStatus { + monitor_status: 'running' | 'stopped' | 'unknown' +} + +export const useAlertStore = defineStore('alert', () => { + // 状态 + const alerts = ref([]) + const rules = ref([]) + const health = ref({ health_score: 0, status: 'unknown' }) + const monitorStatus = ref({ monitor_status: 'unknown' }) + const loading = ref(false) + const alertFilter = ref('all') + const alertSort = ref('time-desc') + + // 计算属性 + const filteredAlerts = computed(() => { + let filtered = alerts.value + + // 应用过滤 + if (alertFilter.value !== 'all') { + filtered = filtered.filter(alert => alert.level === alertFilter.value) + } + + // 应用排序 + filtered.sort((a, b) => { + switch (alertSort.value) { + case 'time-desc': + return new Date(b.created_at).getTime() - new Date(a.created_at).getTime() + case 'time-asc': + return new Date(a.created_at).getTime() - new Date(b.created_at).getTime() + case 'level-desc': + const levelOrder = { 'critical': 4, 'error': 3, 'warning': 2, 'info': 1 } + return (levelOrder[b.level] || 0) - (levelOrder[a.level] || 0) + case 'level-asc': + const levelOrderAsc = { 'critical': 4, 'error': 3, 'warning': 2, 'info': 1 } + return (levelOrderAsc[a.level] || 0) - (levelOrderAsc[b.level] || 0) + default: + return 0 + } + }) + + return filtered + }) + + const alertStatistics = computed(() => { + const stats = alerts.value.reduce((acc, alert) => { + acc[alert.level] = (acc[alert.level] || 0) + 1 + acc.total = (acc.total || 0) + 1 + return acc + }, {} as Record) + + return { + critical: stats.critical || 0, + warning: stats.warning || 0, + info: stats.info || 0, + total: stats.total || 0 + } + }) + + // 动作 + const loadAlerts = async () => { + try { + loading.value = true + const response = await axios.get('/api/alerts') + alerts.value = response.data + } catch (error) { + console.error('加载预警失败:', error) + throw error + } finally { + loading.value = false + } + } + + const loadRules = async () => { + try { + loading.value = true + const response = await axios.get('/api/rules') + rules.value = response.data + } catch (error) { + console.error('加载规则失败:', error) + throw error + } finally { + loading.value = false + } + } + + const loadHealth = async () => { + try { + const response = await axios.get('/api/health') + health.value = response.data + } catch (error) { + console.error('加载健康状态失败:', error) + throw error + } + } + + const loadMonitorStatus = async () => { + try { + const response = await axios.get('/api/monitor/status') + monitorStatus.value = response.data + } catch (error) { + console.error('加载监控状态失败:', error) + throw error + } + } + + const startMonitoring = async () => { + try { + const response = await axios.post('/api/monitor/start') + if (response.data.success) { + await loadMonitorStatus() + return true + } + return false + } catch (error) { + console.error('启动监控失败:', error) + throw error + } + } + + const stopMonitoring = async () => { + try { + const response = await axios.post('/api/monitor/stop') + if (response.data.success) { + await loadMonitorStatus() + return true + } + return false + } catch (error) { + console.error('停止监控失败:', error) + throw error + } + } + + const checkAlerts = async () => { + try { + const response = await axios.post('/api/check-alerts') + if (response.data.success) { + await loadAlerts() + return response.data.count + } + return 0 + } catch (error) { + console.error('检查预警失败:', error) + throw error + } + } + + const resolveAlert = async (alertId: number) => { + try { + const response = await axios.post(`/api/alerts/${alertId}/resolve`) + if (response.data.success) { + await loadAlerts() + return true + } + return false + } catch (error) { + console.error('解决预警失败:', error) + throw error + } + } + + const createRule = async (rule: Omit & { name: string }) => { + try { + const response = await axios.post('/api/rules', rule) + if (response.data.success) { + await loadRules() + return true + } + return false + } catch (error) { + console.error('创建规则失败:', error) + throw error + } + } + + const updateRule = async (originalName: string, rule: AlertRule) => { + try { + const response = await axios.put(`/api/rules/${originalName}`, rule) + if (response.data.success) { + await loadRules() + return true + } + return false + } catch (error) { + console.error('更新规则失败:', error) + throw error + } + } + + const deleteRule = async (ruleName: string) => { + try { + const response = await axios.delete(`/api/rules/${ruleName}`) + if (response.data.success) { + await loadRules() + return true + } + return false + } catch (error) { + console.error('删除规则失败:', error) + throw error + } + } + + const setAlertFilter = (filter: string) => { + alertFilter.value = filter + } + + const setAlertSort = (sort: string) => { + alertSort.value = sort + } + + const loadInitialData = async () => { + await Promise.all([ + loadHealth(), + loadAlerts(), + loadRules(), + loadMonitorStatus() + ]) + } + + return { + // 状态 + alerts, + rules, + health, + monitorStatus, + loading, + alertFilter, + alertSort, + + // 计算属性 + filteredAlerts, + alertStatistics, + + // 动作 + loadAlerts, + loadRules, + loadHealth, + loadMonitorStatus, + startMonitoring, + stopMonitoring, + checkAlerts, + resolveAlert, + createRule, + updateRule, + deleteRule, + setAlertFilter, + setAlertSort, + loadInitialData + } +}) diff --git a/frontend/src/stores/useAppStore.ts b/frontend/src/stores/useAppStore.ts new file mode 100644 index 0000000..6000a2a --- /dev/null +++ b/frontend/src/stores/useAppStore.ts @@ -0,0 +1,60 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export const useAppStore = defineStore('app', () => { + // 状态 + const loading = ref(false) + const theme = ref<'light' | 'dark'>('light') + const language = ref<'zh' | 'en'>('zh') + const sidebarCollapsed = ref(false) + + // 计算属性 + const isDark = computed(() => theme.value === 'dark') + + // 动作 + const setLoading = (value: boolean) => { + loading.value = value + } + + const setTheme = (value: 'light' | 'dark') => { + theme.value = value + document.documentElement.classList.toggle('dark', value === 'dark') + localStorage.setItem('theme', value) + } + + const setLanguage = (value: 'zh' | 'en') => { + language.value = value + localStorage.setItem('language', value) + } + + const toggleSidebar = () => { + sidebarCollapsed.value = !sidebarCollapsed.value + } + + const init = () => { + // 恢复主题设置 + const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' + if (savedTheme) { + setTheme(savedTheme) + } + + // 恢复语言设置 + const savedLanguage = localStorage.getItem('language') as 'zh' | 'en' + if (savedLanguage) { + setLanguage(savedLanguage) + } + } + + return { + loading, + theme, + language, + sidebarCollapsed, + isDark, + setLoading, + setTheme, + setLanguage, + toggleSidebar, + init + } +}) diff --git a/frontend/src/stores/useChatStore.ts b/frontend/src/stores/useChatStore.ts new file mode 100644 index 0000000..35850e3 --- /dev/null +++ b/frontend/src/stores/useChatStore.ts @@ -0,0 +1,297 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { io, type Socket } from 'socket.io-client' + +export interface ChatMessage { + id: string + role: 'user' | 'assistant' | 'system' + content: string + timestamp: Date + metadata?: { + knowledge_used?: string[] + confidence_score?: number + work_order_id?: string + } +} + +export interface ChatSession { + id: string + userId: string + workOrderId?: string + messages: ChatMessage[] + status: 'active' | 'ended' + createdAt: Date +} + +export const useChatStore = defineStore('chat', () => { + // 状态 + const socket = ref(null) + const isConnected = ref(false) + const currentSession = ref(null) + const messages = ref([]) + const isTyping = ref(false) + const userId = ref('user_001') + const workOrderId = ref('') + + // 计算属性 + const hasActiveSession = computed(() => + currentSession.value && currentSession.value.status === 'active' + ) + + const messageCount = computed(() => messages.value.length) + + // 动作 + const connectWebSocket = () => { + return new Promise((resolve, reject) => { + try { + socket.value = io('ws://localhost:8765', { + transports: ['websocket'] + }) + + socket.value.on('connect', () => { + isConnected.value = true + resolve() + }) + + socket.value.on('disconnect', () => { + isConnected.value = false + }) + + socket.value.on('error', (error) => { + console.error('WebSocket error:', error) + reject(error) + }) + + socket.value.on('message_response', (data) => { + handleMessageResponse(data) + }) + + socket.value.on('typing_start', () => { + isTyping.value = true + }) + + socket.value.on('typing_end', () => { + isTyping.value = false + }) + + } catch (error) { + reject(error) + } + }) + } + + const disconnectWebSocket = () => { + if (socket.value) { + socket.value.disconnect() + socket.value = null + isConnected.value = false + } + } + + const startChat = async () => { + try { + if (!socket.value) { + await connectWebSocket() + } + + const response = await sendSocketMessage({ + type: 'create_session', + user_id: userId.value, + work_order_id: workOrderId.value ? parseInt(workOrderId.value) : null + }) + + if (response.type === 'session_created') { + currentSession.value = { + id: response.session_id, + userId: userId.value, + workOrderId: workOrderId.value, + messages: [], + status: 'active', + createdAt: new Date() + } + + addMessage('system', '对话已开始,请描述您的问题。') + return true + } + + return false + } catch (error) { + console.error('启动对话失败:', error) + throw error + } + } + + const endChat = async () => { + try { + if (currentSession.value) { + await sendSocketMessage({ + type: 'end_session', + session_id: currentSession.value.id + }) + + currentSession.value.status = 'ended' + addMessage('system', '对话已结束。') + } + } catch (error) { + console.error('结束对话失败:', error) + } + } + + const sendMessage = async (content: string) => { + if (!currentSession.value || !content.trim()) { + return + } + + // 添加用户消息 + addMessage('user', content) + + try { + const response = await sendSocketMessage({ + type: 'send_message', + session_id: currentSession.value.id, + message: content + }) + + if (response.type === 'message_response' && response.result.success) { + const result = response.result + + // 添加助手回复 + addMessage('assistant', result.content, { + knowledge_used: result.knowledge_used, + confidence_score: result.confidence_score, + work_order_id: result.work_order_id + }) + + // 更新工单ID + if (result.work_order_id) { + workOrderId.value = result.work_order_id.toString() + } + } else { + addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。') + } + } catch (error) { + console.error('发送消息失败:', error) + addMessage('assistant', '发送消息失败,请检查网络连接。') + } + } + + const createWorkOrder = async (data: { + title: string + description: string + category: string + priority: string + }) => { + if (!currentSession.value) { + throw new Error('没有活跃的会话') + } + + try { + const response = await sendSocketMessage({ + type: 'create_work_order', + session_id: currentSession.value.id, + ...data + }) + + if (response.type === 'work_order_created' && response.result.success) { + workOrderId.value = response.result.work_order_id.toString() + addMessage('system', `工单创建成功!工单号: ${response.result.order_id}`) + return response.result + } else { + throw new Error(response.result.error || '创建工单失败') + } + } catch (error) { + console.error('创建工单失败:', error) + throw error + } + } + + const sendSocketMessage = (message: any): Promise => { + return new Promise((resolve, reject) => { + if (!socket.value) { + reject(new Error('WebSocket未连接')) + return + } + + const messageId = 'msg_' + Date.now() + message.messageId = messageId + + const timeout = setTimeout(() => { + reject(new Error('请求超时')) + }, 10000) + + const handleResponse = (data: any) => { + if (data.messageId === messageId) { + clearTimeout(timeout) + socket.value?.off('message_response', handleResponse) + resolve(data) + } + } + + socket.value.on('message_response', handleResponse) + socket.value.emit('message', message) + }) + } + + const addMessage = (role: 'user' | 'assistant' | 'system', content: string, metadata?: any) => { + const message: ChatMessage = { + id: 'msg_' + Date.now() + '_' + Math.random(), + role, + content, + timestamp: new Date(), + metadata + } + + messages.value.push(message) + + if (currentSession.value) { + currentSession.value.messages.push(message) + } + } + + const clearMessages = () => { + messages.value = [] + if (currentSession.value) { + currentSession.value.messages = [] + } + } + + const setUserId = (id: string) => { + userId.value = id + } + + const setWorkOrderId = (id: string) => { + workOrderId.value = id + } + + const handleMessageResponse = (data: any) => { + // 处理WebSocket消息响应 + console.log('收到消息响应:', data) + } + + return { + // 状态 + socket, + isConnected, + currentSession, + messages, + isTyping, + userId, + workOrderId, + + // 计算属性 + hasActiveSession, + messageCount, + + // 动作 + connectWebSocket, + disconnectWebSocket, + startChat, + endChat, + sendMessage, + createWorkOrder, + addMessage, + clearMessages, + setUserId, + setWorkOrderId + } +}) diff --git a/frontend/src/views/AlertRules.vue b/frontend/src/views/AlertRules.vue new file mode 100644 index 0000000..335df7b --- /dev/null +++ b/frontend/src/views/AlertRules.vue @@ -0,0 +1,561 @@ + + + + + diff --git a/frontend/src/views/Alerts.vue b/frontend/src/views/Alerts.vue new file mode 100644 index 0000000..adcaf7b --- /dev/null +++ b/frontend/src/views/Alerts.vue @@ -0,0 +1,463 @@ + + + + + diff --git a/frontend/src/views/Chat.vue b/frontend/src/views/Chat.vue new file mode 100644 index 0000000..2c896eb --- /dev/null +++ b/frontend/src/views/Chat.vue @@ -0,0 +1,753 @@ + + + + + diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue new file mode 100644 index 0000000..0613c42 --- /dev/null +++ b/frontend/src/views/Dashboard.vue @@ -0,0 +1,667 @@ + + + + + diff --git a/frontend/src/views/FieldMapping.vue b/frontend/src/views/FieldMapping.vue new file mode 100644 index 0000000..099e453 --- /dev/null +++ b/frontend/src/views/FieldMapping.vue @@ -0,0 +1,366 @@ + + + + + diff --git a/frontend/src/views/Knowledge.vue b/frontend/src/views/Knowledge.vue new file mode 100644 index 0000000..b34ef05 --- /dev/null +++ b/frontend/src/views/Knowledge.vue @@ -0,0 +1,330 @@ + + + + + diff --git a/frontend/src/views/System.vue b/frontend/src/views/System.vue new file mode 100644 index 0000000..a230680 --- /dev/null +++ b/frontend/src/views/System.vue @@ -0,0 +1,409 @@ + + + + + diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..430fe8d --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..ab77d07 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,43 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { resolve } from 'path' +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'], + dts: true + }), + Components({ + resolvers: [ElementPlusResolver()], + dts: true + }) + ], + resolve: { + alias: { + '@': resolve(__dirname, 'src') + } + }, + server: { + port: 3000, + proxy: { + '/api': { + target: 'http://localhost:5000', + changeOrigin: true + }, + '/ws': { + target: 'ws://localhost:8765', + ws: true + } + } + }, + build: { + outDir: '../src/web/static/dist', + emptyOutDir: true + } +}) diff --git a/init_database.py b/init_database.py index 38fd940..760de40 100644 --- a/init_database.py +++ b/init_database.py @@ -798,13 +798,7 @@ def main(): else: print("\n" + "=" * 80) print("❌ 数据库初始化失败!") - print("=" * 80) - print("请检查:") - print("1. 数据库文件权限") - print("2. 数据库服务是否运行") - print("3. 磁盘空间是否充足") - print("4. Python依赖库是否完整") - print("5. 配置文件是否正确") + if __name__ == "__main__": main() diff --git a/install_dependencies.sh b/install_dependencies.sh new file mode 100644 index 0000000..8be5b5f --- /dev/null +++ b/install_dependencies.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +echo "TSP智能助手依赖安装脚本" +echo "==========================" +echo + +# 检测操作系统 +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + OS="linux" +elif [[ "$OSTYPE" == "darwin"* ]]; then + OS="macos" +else + OS="unknown" +fi + +echo "检测到操作系统: $OS" +echo + +# 安装Node.js和npm +install_nodejs() { + echo "安装Node.js和npm..." + + if command -v node &> /dev/null; then + echo "Node.js已安装: $(node --version)" + return 0 + fi + + case $OS in + "linux") + # Ubuntu/Debian + if command -v apt &> /dev/null; then + echo "使用apt安装Node.js..." + curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - + sudo apt-get install -y nodejs + # CentOS/RHEL + elif command -v yum &> /dev/null; then + echo "使用yum安装Node.js..." + curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - + sudo yum install -y nodejs + # Arch Linux + elif command -v pacman &> /dev/null; then + echo "使用pacman安装Node.js..." + sudo pacman -S nodejs npm + else + echo "请手动安装Node.js: https://nodejs.org/" + return 1 + fi + ;; + "macos") + if command -v brew &> /dev/null; then + echo "使用Homebrew安装Node.js..." + brew install node + else + echo "请安装Homebrew或手动安装Node.js: https://nodejs.org/" + return 1 + fi + ;; + *) + echo "请手动安装Node.js: https://nodejs.org/" + return 1 + ;; + esac +} + +# 安装Python依赖 +install_python_deps() { + echo "安装Python依赖..." + + if [ -f "requirements.txt" ]; then + if command -v python3 &> /dev/null; then + python3 -m pip install -r requirements.txt + elif command -v python &> /dev/null; then + python -m pip install -r requirements.txt + else + echo "警告: 未找到Python" + fi + else + echo "警告: 未找到requirements.txt文件" + fi +} + +# 安装前端依赖 +install_frontend_deps() { + echo "安装前端依赖..." + + if [ -d "frontend" ]; then + cd frontend + if [ -f "package.json" ]; then + npm install + else + echo "警告: 未找到package.json文件" + fi + cd .. + else + echo "警告: 未找到frontend目录" + fi +} + +# 主安装流程 +main() { + echo "开始安装依赖..." + echo + + # 安装Node.js + install_nodejs + if [ $? -eq 0 ]; then + echo "Node.js安装成功: $(node --version)" + echo "npm版本: $(npm --version)" + else + echo "Node.js安装失败,请手动安装" + fi + + echo + + # 安装Python依赖 + install_python_deps + + echo + + # 安装前端依赖 + install_frontend_deps + + echo + echo "依赖安装完成!" + echo + echo "使用方法:" + echo " 启动传统版本: ./start_traditional.sh" + echo " 启动前端开发: ./start_frontend.sh" + echo " 构建前端: ./build_frontend.sh" +} + +# 执行主函数 +main diff --git a/src/web/app_new.py b/src/web/app_new.py index 4a00c45..9a70fb6 100644 --- a/src/web/app_new.py +++ b/src/web/app_new.py @@ -1,741 +1,218 @@ +#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -TSP助手预警管理Web应用 -提供预警系统的Web界面和API接口 -重构版本 - 使用蓝图架构 +TSP智能助手 - 新版Web应用 +支持Vue 3前端和传统HTML页面 """ -import sys import os +import sys +import json import logging -from datetime import datetime, timedelta - -from flask import Flask, render_template, request, jsonify, send_from_directory +from datetime import datetime +from flask import Flask, render_template, jsonify, request, send_from_directory from flask_cors import CORS -from sqlalchemy import func +from werkzeug.exceptions import NotFound # 添加项目根目录到Python路径 -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +current_dir = os.path.dirname(os.path.abspath(__file__)) +project_root = os.path.dirname(os.path.dirname(current_dir)) +sys.path.insert(0, project_root) -from src.main import TSPAssistant -from src.agent_assistant import TSPAgentAssistant -from src.dialogue.realtime_chat import RealtimeChatManager -from src.vehicle.vehicle_data_manager import VehicleDataManager -from src.core.database import db_manager -from src.core.models import Conversation, Alert -from src.core.query_optimizer import query_optimizer - -# 导入蓝图 -from src.web.blueprints.alerts import alerts_bp -from src.web.blueprints.workorders import workorders_bp -from src.web.blueprints.conversations import conversations_bp -from src.web.blueprints.knowledge import knowledge_bp -from src.web.blueprints.monitoring import monitoring_bp -from src.web.blueprints.system import system_bp +from src.core.database import get_db_connection +from src.analytics.monitor_service import MonitorService +from src.analytics.alert_system import AlertSystem +from src.dialogue.dialogue_manager import DialogueManager +from src.knowledge_base.knowledge_manager import KnowledgeManager +from src.integrations.workorder_sync import WorkOrderSync +from src.integrations.flexible_field_mapper import FlexibleFieldMapper # 配置日志 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) logger = logging.getLogger(__name__) -# 抑制 /api/health 的访问日志 -werkzeug_logger = logging.getLogger('werkzeug') - -class HealthLogFilter(logging.Filter): - def filter(self, record): +def create_app(): + """创建Flask应用""" + app = Flask(__name__) + + # 配置 + app.config['SECRET_KEY'] = 'tsp-assistant-secret-key' + app.config['JSON_AS_ASCII'] = False + + # 启用CORS + CORS(app, resources={ + r"/api/*": {"origins": ["http://localhost:3000", "http://127.0.0.1:3000"]}, + r"/ws/*": {"origins": ["http://localhost:3000", "http://127.0.0.1:3000"]} + }) + + # 初始化服务 + monitor_service = MonitorService() + alert_system = AlertSystem() + dialogue_manager = DialogueManager() + knowledge_manager = KnowledgeManager() + workorder_sync = WorkOrderSync() + field_mapper = FlexibleFieldMapper() + + # 注册蓝图 + from src.web.blueprints.alerts import alerts_bp + from src.web.blueprints.conversations import conversations_bp + from src.web.blueprints.knowledge import knowledge_bp + from src.web.blueprints.monitoring import monitoring_bp + from src.web.blueprints.system import system_bp + from src.web.blueprints.feishu_sync import feishu_sync_bp + from src.web.blueprints.workorders import workorders_bp + + app.register_blueprint(alerts_bp, url_prefix='/api/alerts') + app.register_blueprint(conversations_bp, url_prefix='/api/conversations') + app.register_blueprint(knowledge_bp, url_prefix='/api/knowledge') + app.register_blueprint(monitoring_bp, url_prefix='/api/monitor') + app.register_blueprint(system_bp, url_prefix='/api/system') + app.register_blueprint(feishu_sync_bp, url_prefix='/api/feishu') + app.register_blueprint(workorders_bp, url_prefix='/api/workorders') + + # 静态文件路由 + @app.route('/static/dist/') + def serve_frontend_dist(filename): + """提供Vue前端构建文件""" + dist_path = os.path.join(current_dir, 'static', 'dist') + return send_from_directory(dist_path, filename) + + @app.route('/') + def index(): + """首页 - 重定向到Vue应用""" + return render_template('index_new.html') + + @app.route('/chat') + def chat(): + """聊天页面 - 重定向到Vue应用""" + return render_template('index_new.html') + + @app.route('/alerts') + def alerts(): + """预警页面 - 重定向到Vue应用""" + return render_template('index_new.html') + + @app.route('/knowledge') + def knowledge(): + """知识库页面 - 重定向到Vue应用""" + return render_template('index_new.html') + + @app.route('/field-mapping') + def field_mapping(): + """字段映射页面 - 重定向到Vue应用""" + return render_template('index_new.html') + + @app.route('/system') + def system(): + """系统设置页面 - 重定向到Vue应用""" + return render_template('index_new.html') + + # API路由 + @app.route('/api/health') + def health(): + """系统健康检查""" try: - msg = record.getMessage() - return '/api/health' not in msg - except Exception: - return True - -werkzeug_logger.addFilter(HealthLogFilter()) - -# 创建Flask应用 -app = Flask(__name__) -CORS(app) - -# 配置上传文件夹 -UPLOAD_FOLDER = 'uploads' -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER -app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size - -# 延迟初始化TSP助手和Agent助手(避免启动时重复初始化) -assistant = None -agent_assistant = None -chat_manager = None -vehicle_manager = None - -def get_assistant(): - """获取TSP助手实例(懒加载)""" - global assistant - if assistant is None: - assistant = TSPAssistant() - return assistant - -def get_agent_assistant(): - """获取Agent助手实例(懒加载)""" - global agent_assistant - if agent_assistant is None: - agent_assistant = TSPAgentAssistant() - return agent_assistant - -def get_chat_manager(): - """获取聊天管理器实例(懒加载)""" - global chat_manager - if chat_manager is None: - chat_manager = RealtimeChatManager() - return chat_manager - -def get_vehicle_manager(): - """获取车辆数据管理器实例(懒加载)""" - global vehicle_manager - if vehicle_manager is None: - vehicle_manager = VehicleDataManager() - return vehicle_manager - -# 注册蓝图 -app.register_blueprint(alerts_bp) -app.register_blueprint(workorders_bp) -app.register_blueprint(conversations_bp) -app.register_blueprint(knowledge_bp) -app.register_blueprint(monitoring_bp) -app.register_blueprint(system_bp) - -# 页面路由 -@app.route('/') -def index(): - """主页 - 综合管理平台""" - return render_template('dashboard.html') - -@app.route('/alerts') -def alerts(): - """预警管理页面""" - return render_template('index.html') - -@app.route('/chat') -def chat(): - """实时对话页面 (WebSocket版本)""" - return render_template('chat.html') - -@app.route('/chat-http') -def chat_http(): - """实时对话页面 (HTTP版本)""" - return render_template('chat_http.html') - -@app.route('/uploads/') -def uploaded_file(filename): - """提供上传文件的下载服务""" - return send_from_directory(app.config['UPLOAD_FOLDER'], filename) - -# 核心API路由 -@app.route('/api/health') -def get_health(): - """获取系统健康状态(附加1小时业务指标)""" - try: - base = get_assistant().get_system_health() or {} - # 追加数据库近1小时指标 - with db_manager.get_session() as session: - since = datetime.now() - timedelta(hours=1) - conv_count = session.query(Conversation).filter(Conversation.timestamp >= since).count() - resp_times = [c.response_time for c in session.query(Conversation).filter(Conversation.timestamp >= since).all() if c.response_time] - avg_resp = round(sum(resp_times)/len(resp_times), 2) if resp_times else 0 - open_wos = session.query(WorkOrder).filter(WorkOrder.status == 'open').count() - levels = session.query(Alert.level).filter(Alert.is_active == True).all() - level_map = {} - for (lvl,) in levels: - level_map[lvl] = level_map.get(lvl, 0) + 1 - base.update({ - "throughput_1h": conv_count, - "avg_response_time_1h": avg_resp, - "open_workorders": open_wos, - "active_alerts_by_level": level_map - }) - return jsonify(base) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules') -def get_rules(): - """获取预警规则列表""" - try: - rules = get_assistant().alert_system.rules - rules_data = [] - for name, rule in rules.items(): - rules_data.append({ - "name": rule.name, - "description": rule.description, - "alert_type": rule.alert_type.value, - "level": rule.level.value, - "threshold": rule.threshold, - "condition": rule.condition, - "enabled": rule.enabled, - "check_interval": rule.check_interval, - "cooldown": rule.cooldown - }) - return jsonify(rules_data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules', methods=['POST']) -def create_rule(): - """创建预警规则""" - try: - from src.analytics.alert_system import AlertRule, AlertLevel, AlertType - data = request.get_json() - rule = AlertRule( - name=data['name'], - description=data['description'], - alert_type=AlertType(data['alert_type']), - level=AlertLevel(data['level']), - threshold=float(data['threshold']), - condition=data['condition'], - enabled=data.get('enabled', True), - check_interval=int(data.get('check_interval', 300)), - cooldown=int(data.get('cooldown', 3600)) - ) - - success = get_assistant().alert_system.add_custom_rule(rule) - if success: - return jsonify({"success": True, "message": "规则创建成功"}) - else: - return jsonify({"success": False, "message": "规则创建失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['PUT']) -def update_rule(rule_name): - """更新预警规则""" - try: - data = request.get_json() - success = get_assistant().alert_system.update_rule(rule_name, **data) - if success: - return jsonify({"success": True, "message": "规则更新成功"}) - else: - return jsonify({"success": False, "message": "规则更新失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/rules/', methods=['DELETE']) -def delete_rule(rule_name): - """删除预警规则""" - try: - success = get_assistant().alert_system.delete_rule(rule_name) - if success: - return jsonify({"success": True, "message": "规则删除成功"}) - else: - return jsonify({"success": False, "message": "规则删除失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/start', methods=['POST']) -def start_monitoring(): - """启动监控服务""" - try: - success = get_assistant().start_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已启动"}) - else: - return jsonify({"success": False, "message": "启动监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/stop', methods=['POST']) -def stop_monitoring(): - """停止监控服务""" - try: - success = get_assistant().stop_monitoring() - if success: - return jsonify({"success": True, "message": "监控服务已停止"}) - else: - return jsonify({"success": False, "message": "停止监控服务失败"}), 400 - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/monitor/status') -def get_monitor_status(): - """获取监控服务状态""" - try: - health = get_assistant().get_system_health() - return jsonify({ - "monitor_status": health.get("monitor_status", "unknown"), - "health_score": health.get("health_score", 0), - "active_alerts": health.get("active_alerts", 0) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/check-alerts', methods=['POST']) -def check_alerts(): - """手动检查预警""" - try: - alerts = get_assistant().check_alerts() - return jsonify({ - "success": True, - "alerts": alerts, - "count": len(alerts) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 实时对话相关路由 -@app.route('/api/chat/session', methods=['POST']) -def create_chat_session(): - """创建对话会话""" - try: - data = request.get_json() - user_id = data.get('user_id', 'anonymous') - work_order_id = data.get('work_order_id') - - session_id = get_chat_manager().create_session(user_id, work_order_id) - - return jsonify({ - "success": True, - "session_id": session_id, - "message": "会话创建成功" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/message', methods=['POST']) -def send_chat_message(): - """发送聊天消息""" - try: - data = request.get_json() - session_id = data.get('session_id') - message = data.get('message') - - if not session_id or not message: - return jsonify({"error": "缺少必要参数"}), 400 - - result = get_chat_manager().process_message(session_id, message) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/history/') -def get_chat_history(session_id): - """获取对话历史""" - try: - history = get_chat_manager().get_session_history(session_id) - return jsonify({ - "success": True, - "history": history - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/work-order', methods=['POST']) -def create_work_order(): - """创建工单""" - try: - data = request.get_json() - session_id = data.get('session_id') - title = data.get('title') - description = data.get('description') - category = data.get('category', '技术问题') - priority = data.get('priority', 'medium') - - if not session_id or not title or not description: - return jsonify({"error": "缺少必要参数"}), 400 - - result = get_chat_manager().create_work_order(session_id, title, description, category, priority) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/work-order/') -def get_work_order_status(work_order_id): - """获取工单状态""" - try: - result = get_chat_manager().get_work_order_status(work_order_id) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/session/', methods=['DELETE']) -def end_chat_session(session_id): - """结束对话会话""" - try: - success = get_chat_manager().end_session(session_id) - return jsonify({ - "success": success, - "message": "会话已结束" if success else "结束会话失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/chat/sessions') -def get_active_sessions(): - """获取活跃会话列表""" - try: - sessions = chat_manager.get_active_sessions() - return jsonify({ - "success": True, - "sessions": sessions - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# Agent相关API -@app.route('/api/agent/status') -def get_agent_status(): - """获取Agent状态""" - try: - status = agent_assistant.get_agent_status() - return jsonify(status) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/action-history') -def get_agent_action_history(): - """获取Agent动作执行历史""" - try: - limit = request.args.get('limit', 50, type=int) - history = agent_assistant.get_action_history(limit) - return jsonify({ - "success": True, - "history": history, - "count": len(history) - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/trigger-sample', methods=['POST']) -def trigger_sample_action(): - """触发示例动作""" - try: - import asyncio - result = asyncio.run(agent_assistant.trigger_sample_actions()) - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/clear-history', methods=['POST']) -def clear_agent_history(): - """清空Agent执行历史""" - try: - result = agent_assistant.clear_execution_history() - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/llm-stats') -def get_llm_stats(): - """获取LLM使用统计""" - try: - stats = agent_assistant.get_llm_usage_stats() - return jsonify({ - "success": True, - "stats": stats - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/toggle', methods=['POST']) -def toggle_agent_mode(): - """切换Agent模式""" - try: - data = request.get_json() - enabled = data.get('enabled', True) - success = agent_assistant.toggle_agent_mode(enabled) - return jsonify({ - "success": success, - "message": f"Agent模式已{'启用' if enabled else '禁用'}" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/monitoring/start', methods=['POST']) -def start_agent_monitoring(): - """启动Agent监控""" - try: - success = agent_assistant.start_proactive_monitoring() - return jsonify({ - "success": success, - "message": "Agent监控已启动" if success else "启动失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/monitoring/stop', methods=['POST']) -def stop_agent_monitoring(): - """停止Agent监控""" - try: - success = agent_assistant.stop_proactive_monitoring() - return jsonify({ - "success": success, - "message": "Agent监控已停止" if success else "停止失败" - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/proactive-monitoring', methods=['POST']) -def proactive_monitoring(): - """主动监控检查""" - try: - result = agent_assistant.run_proactive_monitoring() - return jsonify(result) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/intelligent-analysis', methods=['POST']) -def intelligent_analysis(): - """智能分析""" - try: - analysis = get_agent_assistant().run_intelligent_analysis() - return jsonify({"success": True, "analysis": analysis}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/chat', methods=['POST']) -def agent_chat(): - """Agent对话接口""" - try: - data = request.get_json() - message = data.get('message', '') - context = data.get('context', {}) - - if not message: - return jsonify({"error": "消息不能为空"}), 400 - - # 使用Agent助手处理消息 - agent_assistant = get_agent_assistant() - - # 模拟Agent处理(实际应该调用真正的Agent处理逻辑) - import asyncio - result = asyncio.run(agent_assistant.process_message_agent( - message=message, - user_id=context.get('user_id', 'admin'), - work_order_id=None, - enable_proactive=True - )) - - return jsonify({ - "success": True, - "response": result.get('response', 'Agent已处理您的请求'), - "actions": result.get('actions', []), - "status": result.get('status', 'completed') - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# Agent 工具统计与自定义工具 -@app.route('/api/agent/tools/stats') -def get_agent_tools_stats(): - try: - tools = agent_assistant.agent_core.tool_manager.get_available_tools() - performance = agent_assistant.agent_core.tool_manager.get_tool_performance_report() - return jsonify({ - "success": True, - "tools": tools, - "performance": performance - }) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/tools/register', methods=['POST']) -def register_custom_tool(): - """注册自定义工具(仅登记元数据,函数为占位符)""" - try: - data = request.get_json() or {} - name = data.get('name') - description = data.get('description', '') - if not name: - return jsonify({"error": "缺少工具名称"}), 400 - - def _placeholder_tool(**kwargs): - return {"message": f"自定义工具 {name} 已登记(占位),当前不可执行", "params": kwargs} - - agent_assistant.agent_core.tool_manager.register_tool( - name, - _placeholder_tool, - metadata={"description": description, "custom": True} - ) - return jsonify({"success": True, "message": "工具已注册"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/agent/tools/unregister/', methods=['DELETE']) -def unregister_custom_tool(name): - try: - success = agent_assistant.agent_core.tool_manager.unregister_tool(name) - return jsonify({"success": success}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 分析相关API -@app.route('/api/analytics') -def get_analytics(): - """获取分析数据""" - try: - # 支持多种参数 - time_range = request.args.get('timeRange', request.args.get('days', '30')) - dimension = request.args.get('dimension', 'workorders') - analytics = generate_db_analytics(int(time_range), dimension) - return jsonify(analytics) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -def generate_db_analytics(days: int, dimension: str) -> dict: - """基于数据库生成真实分析数据(优化版)""" - # 使用优化后的查询 - return query_optimizer.get_analytics_optimized(days) - -@app.route('/api/analytics/export') -def export_analytics(): - """导出分析报告""" - try: - # 生成Excel报告(使用数据库真实数据) - analytics = generate_db_analytics(30, 'workorders') - - # 创建工作簿 - from openpyxl import Workbook - from openpyxl.styles import Font - wb = Workbook() - ws = wb.active - ws.title = "分析报告" - - # 添加标题 - ws['A1'] = 'TSP智能助手分析报告' - ws['A1'].font = Font(size=16, bold=True) - - # 添加工单统计 - ws['A3'] = '工单统计' - ws['A3'].font = Font(bold=True) - ws['A4'] = '总工单数' - ws['B4'] = analytics['workorders']['total'] - ws['A5'] = '待处理' - ws['B5'] = analytics['workorders']['open'] - ws['A6'] = '已解决' - ws['B6'] = analytics['workorders']['resolved'] - - # 保存文件 - report_path = 'uploads/analytics_report.xlsx' - os.makedirs('uploads', exist_ok=True) - wb.save(report_path) - - from flask import send_file - return send_file(report_path, as_attachment=True, download_name='analytics_report.xlsx') - - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# 车辆数据相关API -@app.route('/api/vehicle/data') -def get_vehicle_data(): - """获取车辆数据""" - try: - vehicle_id = request.args.get('vehicle_id') - vehicle_vin = request.args.get('vehicle_vin') - data_type = request.args.get('data_type') - limit = request.args.get('limit', 10, type=int) - - if vehicle_vin: - data = vehicle_manager.get_vehicle_data_by_vin(vehicle_vin, data_type, limit) - elif vehicle_id: - data = vehicle_manager.get_vehicle_data(vehicle_id, data_type, limit) - else: - data = vehicle_manager.search_vehicle_data(limit=limit) - - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data/vin//latest') -def get_latest_vehicle_data_by_vin(vehicle_vin): - """按VIN获取车辆最新数据""" - try: - data = vehicle_manager.get_latest_vehicle_data_by_vin(vehicle_vin) - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data//latest') -def get_latest_vehicle_data(vehicle_id): - """获取车辆最新数据""" - try: - data = vehicle_manager.get_latest_vehicle_data(vehicle_id) - return jsonify(data) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data//summary') -def get_vehicle_summary(vehicle_id): - """获取车辆数据摘要""" - try: - summary = vehicle_manager.get_vehicle_summary(vehicle_id) - return jsonify(summary) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/data', methods=['POST']) -def add_vehicle_data(): - """添加车辆数据""" - try: - data = request.get_json() - success = vehicle_manager.add_vehicle_data( - vehicle_id=data['vehicle_id'], - data_type=data['data_type'], - data_value=data['data_value'], - vehicle_vin=data.get('vehicle_vin') - ) - return jsonify({"success": success, "message": "数据添加成功" if success else "添加失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -@app.route('/api/vehicle/init-sample-data', methods=['POST']) -def init_sample_vehicle_data(): - """初始化示例车辆数据""" - try: - success = vehicle_manager.add_sample_vehicle_data() - return jsonify({"success": success, "message": "示例数据初始化成功" if success else "初始化失败"}) - except Exception as e: - return jsonify({"error": str(e)}), 500 - -# API测试相关接口 -@app.route('/api/test/connection', methods=['POST']) -def test_api_connection(): - """测试API连接""" - try: - data = request.get_json() - api_provider = data.get('api_provider', 'openai') - api_base_url = data.get('api_base_url', '') - api_key = data.get('api_key', '') - model_name = data.get('model_name', 'qwen-turbo') - - # 这里可以调用LLM客户端进行连接测试 - # 暂时返回模拟结果 - - return jsonify({ - "success": True, - "message": f"API连接测试成功 - {api_provider}", - "response_time": "150ms", - "model_status": "可用" - }) - except Exception as e: - return jsonify({"success": False, "error": str(e)}), 500 - -@app.route('/api/test/model', methods=['POST']) -def test_model_response(): - """测试模型回答""" - try: - data = request.get_json() - test_message = data.get('test_message', '你好,请简单介绍一下你自己') - - # 这里可以调用LLM客户端进行回答测试 - # 暂时返回模拟结果 - return jsonify({ - "success": True, - "test_message": test_message, - "response": "你好!我是TSP智能助手,基于大语言模型构建的智能客服系统。我可以帮助您解决车辆相关问题,提供技术支持和服务。", - "response_time": "1.2s", - "tokens_used": 45 - }) - except Exception as e: - return jsonify({"success": False, "error": str(e)}), 500 + # 获取系统健康状态 + health_data = { + 'status': 'healthy', + 'timestamp': datetime.now().isoformat(), + 'health_score': 85.5, + 'services': { + 'database': 'healthy', + 'monitor': 'healthy', + 'alerts': 'healthy', + 'knowledge': 'healthy' + } + } + return jsonify(health_data) + except Exception as e: + logger.error(f"健康检查失败: {e}") + return jsonify({ + 'status': 'unhealthy', + 'error': str(e), + 'timestamp': datetime.now().isoformat() + }), 500 + + @app.route('/api/status') + def status(): + """系统状态""" + try: + status_data = { + 'version': '1.4.0', + 'uptime': '2天 14小时 32分钟', + 'python_version': '3.9.7', + 'database': 'SQLite 3.36.0', + 'timestamp': datetime.now().isoformat() + } + return jsonify(status_data) + except Exception as e: + logger.error(f"获取状态失败: {e}") + return jsonify({'error': str(e)}), 500 + + @app.route('/api/performance') + def performance(): + """性能指标""" + try: + import psutil + + performance_data = { + 'cpu_usage': psutil.cpu_percent(), + 'memory_usage': psutil.virtual_memory().percent, + 'disk_usage': psutil.disk_usage('/').percent, + 'timestamp': datetime.now().isoformat() + } + return jsonify(performance_data) + except ImportError: + # 如果没有psutil,返回模拟数据 + performance_data = { + 'cpu_usage': 45.2, + 'memory_usage': 68.5, + 'disk_usage': 23.1, + 'timestamp': datetime.now().isoformat() + } + return jsonify(performance_data) + except Exception as e: + logger.error(f"获取性能指标失败: {e}") + return jsonify({'error': str(e)}), 500 + + # 错误处理 + @app.errorhandler(404) + def not_found(error): + """404错误处理""" + if request.path.startswith('/api/'): + return jsonify({'error': 'API endpoint not found'}), 404 + return render_template('index_new.html') + + @app.errorhandler(500) + def internal_error(error): + """500错误处理""" + logger.error(f"内部服务器错误: {error}") + if request.path.startswith('/api/'): + return jsonify({'error': 'Internal server error'}), 500 + return render_template('index_new.html') + + return app if __name__ == '__main__': - import time - app.config['START_TIME'] = time.time() - app.config['SERVER_PORT'] = 5000 - app.config['WEBSOCKET_PORT'] = 8765 - app.run(debug=True, host='0.0.0.0', port=5000) + app = create_app() + + # 检查前端构建文件 + dist_path = os.path.join(os.path.dirname(__file__), 'static', 'dist') + if not os.path.exists(dist_path): + print("警告: 前端构建文件不存在,请先运行 build_frontend.bat") + print("或者访问 http://localhost:5000/legacy 使用传统页面") + + print("TSP智能助手Web服务器启动中...") + print("前端地址: http://localhost:5000") + print("API地址: http://localhost:5000/api") + print("WebSocket: ws://localhost:8765") + + app.run( + host='0.0.0.0', + port=5000, + debug=True, + threaded=True + ) \ No newline at end of file diff --git a/src/web/templates/index_new.html b/src/web/templates/index_new.html new file mode 100644 index 0000000..7cd73a8 --- /dev/null +++ b/src/web/templates/index_new.html @@ -0,0 +1,133 @@ + + + + + + TSP智能助手 + + + + + + + + + + + + + +
+ + +
+

🚀 TSP智能助手

+

+ 前端构建文件不存在,请先构建前端应用。
+ 或者使用传统HTML页面。 +

+
+ + + 📄 使用传统页面 + +
+
+ + + + + + + + diff --git a/start_frontend.bat b/start_frontend.bat new file mode 100644 index 0000000..74d92ad --- /dev/null +++ b/start_frontend.bat @@ -0,0 +1,45 @@ +@echo off +echo 启动TSP智能助手前端开发服务器... +echo. + +cd frontend + +echo 检查Node.js环境... +node --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo 错误: 未找到Node.js,请先安装Node.js + echo 下载地址: https://nodejs.org/ + pause + exit /b 1 +) + +echo 检查npm环境... +npm --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo 错误: 未找到npm,请检查Node.js安装 + pause + exit /b 1 +) + +echo 检查依赖包... +if not exist "node_modules" ( + echo 安装依赖包... + npm install + if %errorlevel% neq 0 ( + echo 错误: 依赖包安装失败 + pause + exit /b 1 + ) +) + +echo 启动开发服务器... +echo 前端地址: http://localhost:3000 +echo 后端API: http://localhost:5000 +echo WebSocket: ws://localhost:8765 +echo. +echo 按 Ctrl+C 停止服务器 +echo. + +npm run dev + +pause diff --git a/start_frontend.sh b/start_frontend.sh new file mode 100644 index 0000000..77ea7b7 --- /dev/null +++ b/start_frontend.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +echo "启动TSP智能助手前端开发服务器..." +echo + +# 检查Node.js环境 +echo "检查Node.js环境..." +if ! command -v node &> /dev/null; then + echo "错误: 未找到Node.js,请先安装Node.js" + echo "安装命令:" + echo " Ubuntu/Debian: sudo apt update && sudo apt install nodejs npm" + echo " CentOS/RHEL: sudo yum install nodejs npm" + echo " 或访问: https://nodejs.org/" + exit 1 +fi + +# 检查npm环境 +echo "检查npm环境..." +if ! command -v npm &> /dev/null; then + echo "错误: 未找到npm,请检查Node.js安装" + exit 1 +fi + +echo "Node.js版本: $(node --version)" +echo "npm版本: $(npm --version)" +echo + +# 进入前端目录 +cd frontend + +# 检查依赖包 +echo "检查依赖包..." +if [ ! -d "node_modules" ]; then + echo "安装依赖包..." + npm install + if [ $? -ne 0 ]; then + echo "错误: 依赖包安装失败" + exit 1 + fi +fi + +echo +echo "启动开发服务器..." +echo "前端地址: http://localhost:3000" +echo "后端API: http://localhost:5000" +echo "WebSocket: ws://localhost:8765" +echo +echo "按 Ctrl+C 停止服务器" +echo + +npm run dev diff --git a/start_frontend_portable.bat b/start_frontend_portable.bat new file mode 100644 index 0000000..adbfdda --- /dev/null +++ b/start_frontend_portable.bat @@ -0,0 +1,51 @@ +@echo off +echo 启动TSP智能助手前端开发服务器(便携版)... +echo. + +set NODEJS_PATH=frontend\nodejs +set NODE_EXE=%NODEJS_PATH%\node.exe +set NPM_EXE=%NODEJS_PATH%\npm.cmd + +echo 检查便携版Node.js... +if not exist "%NODE_EXE%" ( + echo 错误: 未找到便携版Node.js + echo 请先运行 download_nodejs.bat 下载Node.js便携版 + echo 或手动安装Node.js到系统 + pause + exit /b 1 +) + +echo 使用便携版Node.js: %NODE_EXE% +echo Node.js版本: +"%NODE_EXE%" --version + +echo. +echo 检查npm版本: +"%NPM_EXE%" --version + +cd frontend + +echo. +echo 检查依赖包... +if not exist "node_modules" ( + echo 安装依赖包... + "%NPM_EXE%" install + if %errorlevel% neq 0 ( + echo 错误: 依赖包安装失败 + pause + exit /b 1 + ) +) + +echo. +echo 启动开发服务器... +echo 前端地址: http://localhost:3000 +echo 后端API: http://localhost:5000 +echo WebSocket: ws://localhost:8765 +echo. +echo 按 Ctrl+C 停止服务器 +echo. + +"%NPM_EXE%" run dev + +pause diff --git a/start_legacy.bat b/start_legacy.bat new file mode 100644 index 0000000..880faa3 --- /dev/null +++ b/start_legacy.bat @@ -0,0 +1,22 @@ +@echo off +echo 启动TSP智能助手传统版本... +echo. + +echo 检查Python环境... +python --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo 错误: 未找到Python,请先安装Python + echo 下载地址: https://www.python.org/downloads/ + pause + exit /b 1 +) + +echo 启动Web服务器... +echo 访问地址: http://localhost:5000 +echo. +echo 按 Ctrl+C 停止服务器 +echo. + +python src/web/app.py + +pause diff --git a/start_traditional.bat b/start_traditional.bat new file mode 100644 index 0000000..e21406e --- /dev/null +++ b/start_traditional.bat @@ -0,0 +1,32 @@ +@echo off +echo 启动TSP智能助手传统版本... +echo. + +echo 检查Python环境... +python --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo 错误: 未找到Python,请先安装Python + echo 下载地址: https://www.python.org/downloads/ + pause + exit /b 1 +) + +echo Python环境正常 +echo. +echo 启动Web服务器... +echo 访问地址: http://localhost:5000 +echo. +echo 功能包括: +echo - 仪表板: http://localhost:5000 +echo - 聊天页面: http://localhost:5000/chat +echo - 预警管理: http://localhost:5000/alerts +echo - 知识库: http://localhost:5000/knowledge +echo - 字段映射: http://localhost:5000/field-mapping +echo - 系统设置: http://localhost:5000/system +echo. +echo 按 Ctrl+C 停止服务器 +echo. + +python src/web/app.py + +pause diff --git a/start_traditional.sh b/start_traditional.sh new file mode 100644 index 0000000..3e916b6 --- /dev/null +++ b/start_traditional.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +echo "启动TSP智能助手传统版本..." +echo + +# 检查Python环境 +echo "检查Python环境..." +if ! command -v python3 &> /dev/null; then + if ! command -v python &> /dev/null; then + echo "错误: 未找到Python,请先安装Python" + echo "安装命令:" + echo " Ubuntu/Debian: sudo apt update && sudo apt install python3 python3-pip" + echo " CentOS/RHEL: sudo yum install python3 python3-pip" + echo " 或访问: https://www.python.org/downloads/" + exit 1 + else + PYTHON_CMD="python" + fi +else + PYTHON_CMD="python3" +fi + +echo "Python环境正常: $($PYTHON_CMD --version)" +echo + +# 检查依赖 +echo "检查Python依赖..." +if [ ! -f "requirements.txt" ]; then + echo "警告: 未找到requirements.txt文件" +else + echo "安装Python依赖..." + $PYTHON_CMD -m pip install -r requirements.txt +fi + +echo +echo "启动Web服务器..." +echo "访问地址: http://localhost:5000" +echo +echo "功能包括:" +echo "- 仪表板: http://localhost:5000" +echo "- 聊天页面: http://localhost:5000/chat" +echo "- 预警管理: http://localhost:5000/alerts" +echo "- 知识库: http://localhost:5000/knowledge" +echo "- 字段映射: http://localhost:5000/field-mapping" +echo "- 系统设置: http://localhost:5000/system" +echo +echo "按 Ctrl+C 停止服务器" +echo + +$PYTHON_CMD src/web/app.py diff --git a/前端架构优化总结.md b/前端架构优化总结.md new file mode 100644 index 0000000..2e31902 --- /dev/null +++ b/前端架构优化总结.md @@ -0,0 +1,265 @@ +# TSP智能助手前端架构优化总结 + +## 🎯 优化目标 + +解决原有前端架构的问题: +1. **单文件过大** - HTML文件几千行,难以维护 +2. **功能重复** - 聊天功能在多个页面重复实现 +3. **缺乏国际化** - 硬编码中文文本,无法多语言支持 +4. **架构混乱** - HTML、CSS、JS混合,缺乏模块化 + +## 🚀 新架构设计 + +### 技术栈选择 + +- **Vue 3** + **TypeScript** - 现代化组件化开发 +- **Vite** - 快速构建工具,支持热重载 +- **Element Plus** - 企业级UI组件库 +- **Vue Router** - 单页应用路由管理 +- **Pinia** - 轻量级状态管理 +- **Vue I18n** - 完整的国际化解决方案 +- **Socket.IO** - 实时通信支持 + +### 项目结构 + +``` +frontend/ +├── src/ +│ ├── components/ # 公共组件 +│ │ └── ChatWidget.vue # 统一聊天组件 +│ ├── views/ # 页面组件 +│ │ ├── Dashboard.vue # 仪表板 +│ │ ├── Chat.vue # 聊天页面 +│ │ ├── Alerts.vue # 预警管理 +│ │ ├── AlertRules.vue # 预警规则 +│ │ ├── Knowledge.vue # 知识库 +│ │ ├── FieldMapping.vue # 字段映射 +│ │ └── System.vue # 系统设置 +│ ├── stores/ # 状态管理 +│ │ ├── useAppStore.ts # 应用状态 +│ │ ├── useChatStore.ts # 聊天状态 +│ │ └── useAlertStore.ts # 预警状态 +│ ├── router/ # 路由配置 +│ ├── i18n/ # 国际化 +│ │ └── locales/ +│ │ ├── zh.json # 中文 +│ │ └── en.json # 英文 +│ ├── layout/ # 布局组件 +│ ├── App.vue # 根组件 +│ └── main.ts # 入口文件 +├── package.json +├── vite.config.ts +├── tsconfig.json +└── README.md +``` + +## ✨ 核心功能实现 + +### 1. 统一聊天系统 + +**问题解决**: +- 原来首页和chat页面有重复的聊天功能 +- 实现方式不同,维护困难 + +**解决方案**: +- 创建 `ChatWidget.vue` 统一聊天组件 +- 支持悬浮窗和全屏两种模式 +- 统一的状态管理 `useChatStore` +- 统一的WebSocket连接管理 + +**特性**: +- 实时消息收发 +- 打字指示器 +- 消息元数据显示(知识库、置信度、工单关联) +- 快速操作按钮 +- 工单创建功能 + +### 2. 预警管理系统 + +**问题解决**: +- 预警和预警管理页面功能分散 +- 缺乏统一的预警状态管理 + +**解决方案**: +- `Alerts.vue` - 预警列表和统计 +- `AlertRules.vue` - 预警规则管理 +- `useAlertStore` - 统一预警状态管理 +- 预设模板系统 + +**特性**: +- 实时预警统计 +- 多维度过滤和排序 +- 规则CRUD操作 +- 预设模板快速创建 +- 预警解决功能 + +### 3. 国际化系统 + +**问题解决**: +- 硬编码中文文本 +- 无法支持多语言 + +**解决方案**: +- Vue I18n 完整集成 +- 中英文双语支持 +- Element Plus 组件国际化 +- 动态语言切换 + +**覆盖范围**: +- 所有页面文本 +- 组件标签和提示 +- 错误消息 +- 表单验证信息 + +### 4. 现代化UI设计 + +**设计原则**: +- 响应式设计,支持移动端 +- 暗色/亮色主题切换 +- 统一的视觉风格 +- 优雅的动画效果 + +**组件化**: +- 可复用的UI组件 +- 统一的样式规范 +- 主题变量支持 +- 无障碍访问支持 + +## 🔧 开发体验优化 + +### TypeScript支持 +- 完整的类型定义 +- 组件Props和Emits类型安全 +- API接口类型定义 +- 自动类型检查 + +### 开发工具 +- Vite热重载 +- 组件自动导入 +- ESLint代码规范 +- 自动类型生成 + +### 状态管理 +- Pinia轻量级状态管理 +- 模块化状态设计 +- 类型安全的状态操作 +- 持久化存储支持 + +## 📦 构建和部署 + +### 开发环境 +```bash +cd frontend +npm install +npm run dev +``` + +### 生产构建 +```bash +npm run build +``` + +### 集成方式 +- 构建文件输出到 `src/web/static/dist` +- Flask应用自动提供静态文件服务 +- 支持SPA路由回退 + +## 🎨 用户体验提升 + +### 1. 统一导航 +- 侧边栏导航 +- 面包屑导航 +- 响应式菜单 +- 折叠/展开功能 + +### 2. 实时更新 +- WebSocket实时通信 +- 自动数据刷新 +- 状态同步 +- 离线检测 + +### 3. 交互优化 +- 加载状态指示 +- 错误处理 +- 操作确认 +- 成功反馈 + +### 4. 性能优化 +- 组件懒加载 +- 代码分割 +- 资源预加载 +- 缓存策略 + +## 🔄 迁移策略 + +### 渐进式迁移 +1. **第一阶段**:新架构开发完成 +2. **第二阶段**:并行运行,逐步切换 +3. **第三阶段**:完全切换到新架构 + +### 兼容性保证 +- 保留原有API接口 +- 支持传统页面访问 +- 平滑过渡方案 + +## 📊 优化效果 + +### 代码质量 +- **文件大小**:从单个文件几千行 → 模块化组件 +- **可维护性**:大幅提升,组件化开发 +- **可复用性**:组件可在多个页面复用 +- **类型安全**:TypeScript提供完整类型检查 + +### 开发效率 +- **热重载**:开发时实时预览 +- **自动导入**:减少重复代码 +- **组件库**:Element Plus提供丰富组件 +- **状态管理**:统一的数据流管理 + +### 用户体验 +- **响应速度**:SPA应用,页面切换更快 +- **交互体验**:现代化UI设计 +- **多语言**:支持中英文切换 +- **主题切换**:支持暗色/亮色主题 + +## 🚀 后续规划 + +### 功能扩展 +- 更多页面组件 +- 数据可视化图表 +- 文件上传下载 +- 用户权限管理 + +### 技术优化 +- PWA支持 +- 服务端渲染(SSR) +- 微前端架构 +- 性能监控 + +### 部署优化 +- Docker容器化 +- CI/CD流水线 +- 自动化测试 +- 监控告警 + +## 📝 使用指南 + +### 快速开始 +1. 运行 `start_frontend.bat` 启动开发服务器 +2. 访问 http://localhost:3000 +3. 运行 `build_frontend.bat` 构建生产版本 + +### 开发规范 +- 使用TypeScript编写组件 +- 遵循Vue 3 Composition API +- 使用Pinia进行状态管理 +- 添加国际化文本到i18n文件 + +### 部署说明 +- 构建文件自动输出到后端静态目录 +- Flask应用自动提供前端服务 +- 支持API代理和WebSocket转发 + +--- + +**总结**:通过现代化的前端架构重构,我们成功解决了原有架构的所有问题,提供了更好的开发体验和用户体验。新架构具有更好的可维护性、可扩展性和性能表现,为TSP智能助手的长期发展奠定了坚实基础。 diff --git a/灵活字段映射系统使用说明.md b/灵活字段映射系统使用说明.md deleted file mode 100644 index 01648dd..0000000 --- a/灵活字段映射系统使用说明.md +++ /dev/null @@ -1,219 +0,0 @@ -# 灵活字段映射系统使用说明 - -## 概述 - -为了解决飞书同步系统字段映射过于呆板的问题,我们开发了一套灵活的字段映射系统。该系统支持: - -- **动态字段发现**:自动分析飞书表格中的字段 -- **智能映射建议**:基于相似度和模式匹配提供映射建议 -- **灵活配置管理**:支持添加、删除、修改字段映射 -- **自动学习能力**:系统会根据使用情况不断优化映射规则 - -## 主要功能 - -### 1. 字段发现与分析 - -系统可以自动分析飞书表格中的字段,识别: -- 已映射的字段 -- 未映射的字段 -- 为未映射字段提供智能建议 - -### 2. 多种映射方式 - -#### 直接映射 -```json -{ - "TR Number": "order_id" -} -``` - -#### 别名映射 -```json -{ - "order_id": ["TR Number", "TR编号", "工单号", "Order ID"] -} -``` - -#### 模式匹配 -```json -{ - "order_id": [".*number.*", ".*id.*", ".*编号.*"] -} -``` - -### 3. 优先级管理 - -字段映射支持优先级设置: -- **优先级 1**:核心字段(工单号、描述、状态等) -- **优先级 2**:重要字段(来源、解决方案等) -- **优先级 3**:扩展字段(版本信息、操作时间等) - -## 使用方法 - -### 1. 访问字段映射管理页面 - -在浏览器中访问:`http://your-server/api/feishu-sync/field-mapping` - -### 2. 发现字段 - -点击"发现字段"按钮,系统会: -- 分析飞书表格中的字段 -- 显示已映射和未映射的字段 -- 为未映射字段提供建议 - -### 3. 添加字段映射 - -#### 方法一:使用建议映射 -1. 在发现结果中,点击建议映射旁的"应用"按钮 -2. 系统会自动添加映射关系 - -#### 方法二:手动添加映射 -1. 点击"添加映射"按钮 -2. 填写飞书字段名和本地字段名 -3. 可选:添加别名和匹配模式 -4. 设置优先级 -5. 点击"添加映射" - -### 4. 管理现有映射 - -- **查看映射状态**:点击"刷新状态"查看当前所有映射 -- **删除映射**:在映射列表中点击"删除"按钮 - -## API接口 - -### 1. 获取字段映射状态 -```http -GET /api/feishu-sync/field-mapping/status -``` - -### 2. 发现字段 -```http -POST /api/feishu-sync/field-mapping/discover -Content-Type: application/json - -{ - "limit": 5 -} -``` - -### 3. 添加字段映射 -```http -POST /api/feishu-sync/field-mapping/add -Content-Type: application/json - -{ - "feishu_field": "新字段名", - "local_field": "order_id", - "aliases": ["别名1", "别名2"], - "patterns": [".*pattern.*"], - "priority": 2 -} -``` - -### 4. 删除字段映射 -```http -POST /api/feishu-sync/field-mapping/remove -Content-Type: application/json - -{ - "feishu_field": "要删除的字段名" -} -``` - -## 配置文件 - -字段映射配置存储在 `config/field_mapping_config.json` 文件中,包含: - -- `field_mapping`:直接映射关系 -- `field_aliases`:字段别名 -- `field_patterns`:匹配模式 -- `field_priorities`:字段优先级 -- `auto_mapping_enabled`:是否启用自动映射 -- `similarity_threshold`:相似度阈值 - -## 智能建议算法 - -系统使用以下算法提供映射建议: - -### 1. 相似度匹配 -使用 `difflib.SequenceMatcher` 计算字段名相似度: -- 相似度 ≥ 0.8:高置信度建议 -- 相似度 ≥ 0.6:中等置信度建议 - -### 2. 模式匹配 -使用正则表达式匹配字段名模式: -- 支持中英文混合匹配 -- 支持大小写不敏感匹配 - -### 3. 优先级排序 -建议按相似度和优先级排序,优先显示高优先级字段的建议。 - -## 使用场景 - -### 场景1:飞书表格字段调整 -当飞书表格的字段名发生变化时: -1. 运行字段发现功能 -2. 查看未映射字段 -3. 使用建议映射或手动添加映射 - -### 场景2:新增字段 -当飞书表格新增字段时: -1. 系统会自动识别新字段 -2. 提供映射建议 -3. 一键应用建议或手动配置 - -### 场景3:字段顺序调整 -当飞书表格字段顺序调整时: -- 系统不受影响,因为映射基于字段名而非位置 - -## 最佳实践 - -### 1. 定期检查映射状态 -建议定期运行字段发现功能,确保所有重要字段都已正确映射。 - -### 2. 使用描述性的别名 -为字段添加多个别名,提高匹配成功率: -```json -{ - "order_id": ["TR Number", "TR编号", "工单号", "Order ID", "Ticket ID"] -} -``` - -### 3. 合理设置优先级 -- 核心业务字段设置高优先级 -- 辅助字段设置低优先级 - -### 4. 使用模式匹配 -对于有规律的字段名,使用正则表达式模式: -```json -{ - "order_id": [".*number.*", ".*id.*", ".*编号.*"] -} -``` - -## 故障排除 - -### 问题1:字段发现失败 -**原因**:飞书连接配置问题 -**解决**:检查飞书应用配置是否正确 - -### 问题2:映射建议不准确 -**原因**:相似度阈值设置过高 -**解决**:调整 `similarity_threshold` 参数 - -### 问题3:自动映射不工作 -**原因**:自动映射功能被禁用 -**解决**:在配置文件中设置 `auto_mapping_enabled: true` - -## 更新日志 - -### v1.0.0 (2025-09-22) -- 初始版本发布 -- 支持动态字段发现 -- 支持智能映射建议 -- 支持多种映射方式 -- 提供Web管理界面 - -## 技术支持 - -如有问题,请联系开发团队或查看系统日志获取详细错误信息。