diff --git a/src/core/database.py b/src/core/database.py index 3afae7b..42273ce 100644 --- a/src/core/database.py +++ b/src/core/database.py @@ -95,6 +95,9 @@ class DatabaseManager: ('users', 'tenant_id', "VARCHAR(50) DEFAULT 'default'"), ('alerts', 'tenant_id', "VARCHAR(50) DEFAULT 'default'"), ('analytics', 'tenant_id', "VARCHAR(50) DEFAULT 'default'"), + ('work_order_suggestions', 'tenant_id', "VARCHAR(50) DEFAULT 'default'"), + ('work_order_process_history', 'tenant_id', "VARCHAR(50) DEFAULT 'default'"), + ('vehicle_data', 'tenant_id', "VARCHAR(50) DEFAULT 'default'"), ] for table_name, col_name, col_type in migrations: if table_name in inspector.get_table_names(): diff --git a/src/core/models.py b/src/core/models.py index e8fde89..e38d776 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -183,6 +183,7 @@ class VehicleData(Base): __tablename__ = "vehicle_data" id = Column(Integer, primary_key=True) + tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True) vehicle_id = Column(String(50), nullable=False) # 车辆ID vehicle_vin = Column(String(17)) # 车架号 data_type = Column(String(50), nullable=False) # 数据类型(位置、状态、故障等) @@ -237,6 +238,7 @@ class WorkOrderSuggestion(Base): __tablename__ = "work_order_suggestions" id = Column(Integer, primary_key=True) + tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True) work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=False) ai_suggestion = Column(Text) human_resolution = Column(Text) @@ -251,6 +253,7 @@ class WorkOrderProcessHistory(Base): __tablename__ = "work_order_process_history" id = Column(Integer, primary_key=True) + tenant_id = Column(String(50), nullable=False, default=DEFAULT_TENANT, index=True) work_order_id = Column(Integer, ForeignKey("work_orders.id"), nullable=False) # 处理人员信息 diff --git a/src/dialogue/realtime_chat.py b/src/dialogue/realtime_chat.py index 899dd0d..481b8bd 100644 --- a/src/dialogue/realtime_chat.py +++ b/src/dialogue/realtime_chat.py @@ -309,8 +309,9 @@ class RealtimeChatManager: ) self.message_history[session_id].append(user_msg) - # 搜索知识 + VIN - knowledge_results = self._search_knowledge(user_message) + # 搜索知识 + VIN(按租户隔离) + session_tenant = session.get("tenant_id") + knowledge_results = self._search_knowledge(user_message, tenant_id=session_tenant) vin = self._extract_vin(user_message) if vin: latest = self.vehicle_manager.get_latest_vehicle_data_by_vin(vin) @@ -326,7 +327,7 @@ class RealtimeChatManager: # 流式生成 full_content = [] for chunk in self._generate_response_stream( - user_message, knowledge_results, session["context"], session["work_order_id"] + user_message, knowledge_results, session["context"], session["work_order_id"], tenant_id=session_tenant ): full_content.append(chunk) yield f"data: {json.dumps({'chunk': chunk}, ensure_ascii=False)}\n\n" diff --git a/src/integrations/feishu_longconn_service.py b/src/integrations/feishu_longconn_service.py index e9ee7dc..75af6c3 100644 --- a/src/integrations/feishu_longconn_service.py +++ b/src/integrations/feishu_longconn_service.py @@ -97,17 +97,6 @@ class FeishuLongConnService: # 获取发送者ID和群信息 sender_id_obj = sender.sender_id - # 调试:打印 sender 对象的所有属性 - try: - sender_attrs = {k: getattr(sender_id_obj, k, None) for k in ['user_id', 'open_id', 'union_id'] if getattr(sender_id_obj, k, None)} - if not sender_attrs: - # 尝试 __dict__ 或 dir - sender_attrs = {k: v for k, v in vars(sender_id_obj).items() if v and not k.startswith('_')} if hasattr(sender_id_obj, '__dict__') else {} - logger.info(f" sender_id 属性: {sender_attrs}") - except Exception as e: - logger.warning(f" 无法解析 sender_id 属性: {e}, type={type(sender_id_obj)}") - sender_attrs = {} - sender_open_id = getattr(sender_id_obj, 'open_id', '') or '' sender_user_id = getattr(sender_id_obj, 'user_id', '') or '' sender_union_id = getattr(sender_id_obj, 'union_id', '') or '' diff --git a/src/web/decorators.py b/src/web/decorators.py index fd13398..975658e 100644 --- a/src/web/decorators.py +++ b/src/web/decorators.py @@ -80,53 +80,8 @@ def cache_response(timeout=300): return decorator -def resolve_tenant_id(source='auto'): - """ - 租户 ID 解析装饰器。 - 从请求中提取 tenant_id 并注入到 kwargs['tenant_id']。 - - source: - 'auto' — 依次从 JSON body、query args、session 中查找 - 'query' — 仅从 query args - 'body' — 仅从 JSON body - - 如果未找到,使用 DEFAULT_TENANT 并记录警告。 - """ - import logging - _logger = logging.getLogger('tenant_resolver') - - def decorator(f): - @wraps(f) - def decorated_function(*args, **kwargs): - from flask import request as req - from src.core.models import DEFAULT_TENANT - - tenant_id = None - - if source in ('auto', 'body'): - try: - data = req.get_json(silent=True) - if data: - tenant_id = data.get('tenant_id') - except Exception: - pass - - if not tenant_id and source in ('auto', 'query'): - tenant_id = req.args.get('tenant_id') - - if not tenant_id: - tenant_id = DEFAULT_TENANT - # 只在写操作时警告,读操作不警告(全局查询是合理的) - if req.method in ('POST', 'PUT', 'DELETE'): - _logger.warning( - f"⚠️ API {req.method} {req.path} 未指定 tenant_id,使用默认租户 '{DEFAULT_TENANT}'" - ) - - kwargs['tenant_id'] = tenant_id - return f(*args, **kwargs) - return decorated_function - return decorator - +# resolve_tenant_id 装饰器已移除 — 当前各端点手动处理 tenant_id, +# 未来引入 Repository 层后可在 DAO 层统一拦截。 # 简易内存限流器 _rate_limit_store = {} diff --git a/src/web/static/js/app-new.js b/src/web/static/js/app-new.js deleted file mode 100644 index ce0d2bc..0000000 --- a/src/web/static/js/app-new.js +++ /dev/null @@ -1,130 +0,0 @@ -/** - * 重构后的主应用文件 - * 使用模块化架构整合所有功能 - */ - -// 全局变量声明 -let alertManager; -let healthMonitor; -let agentMonitor; - -// DOM加载完成后初始化应用 -document.addEventListener('DOMContentLoaded', async () => { - try { - // 初始化各个管理器 - alertManager = new AlertManager(); - healthMonitor = new HealthMonitor(); - agentMonitor = new AgentMonitor(); - - // 启动自动刷新 - healthMonitor.startMonitoring(); - - console.log('TSP助手应用初始化完成'); - } catch (error) { - console.error('应用初始化失败:', error); - notificationManager.error('应用初始化失败,请刷新页面重试'); - } -}); - -// 健康监控组件 -class HealthMonitor { - constructor() { - this.interval = null; - } - - startMonitoring() { - // 每5秒检查一次健康状态和监控状态 - this.interval = setInterval(async () => { - try { - const [healthData, monitorData] = await Promise.all([ - apiService.getHealth(), - apiService.getMonitorStatus() - ]); - - store.updateHealth(healthData); - store.updateMonitorStatus(monitorData.monitor_status); - } catch (error) { - console.error('健康检查失败:', error); - } - }, 5000); - } - - stopMonitoring() { - if (this.interval) { - clearInterval(this.interval); - this.interval = null; - } - } -} - -// Agent监控组件 -class AgentMonitor { - constructor() { - this.interval = null; - this.init(); - } - - init() { - // 监听Agent相关按钮 - this.bindAgentControls(); - this.loadAgentStatus(); - } - - bindAgentControls() { - const toggleBtn = document.getElementById('toggle-agent'); - if (toggleBtn) { - toggleBtn.addEventListener('click', () => this.toggleAgent()); - } - } - - async loadAgentStatus() { - try { - const status = await apiService.getAgentStatus(); - store.updateAgentStatus(status); - this.updateAgentDisplay(); - } catch (error) { - console.error('加载Agent状态失败:', error); - } - } - - async toggleAgent() { - try { - const currentStatus = store.getState().agentStatus; - const enabled = currentStatus.status === 'inactive'; - - const result = await apiService.toggleAgent(enabled); - - if (result.success) { - notificationManager.success(`Agent已${enabled ? '启用' : '禁用'}`); - await this.loadAgentStatus(); - } else { - notificationManager.error(result.message || '操作失败'); - } - } catch (error) { - console.error('切换Agent状态失败:', error); - notificationManager.error('操作失败'); - } - } - - updateAgentDisplay() { - const status = store.getState().agentStatus; - const statusElement = document.getElementById('agent-status'); - - if (statusElement) { - const statusText = status.status === 'active' ? '运行中' : '未运行'; - const statusClass = status.status === 'active' ? 'text-success' : 'text-secondary'; - - statusElement.innerHTML = ` - - Agent: ${statusText} - (${status.active_goals} 个活跃目标, ${status.available_tools} 个工具) - `; - } - } -} - -// 导出全局对象供HTML访问 -window.alertManager = alertManager; -window.apiService = apiService; -window.store = store; -window.notificationManager = notificationManager; diff --git a/src/web/static/js/app.js b/src/web/static/js/app.js deleted file mode 100644 index a85c92b..0000000 --- a/src/web/static/js/app.js +++ /dev/null @@ -1,556 +0,0 @@ -// TSP助手预警管理系统前端脚本 - -class AlertManager { - constructor() { - this.alerts = []; - this.rules = []; - this.health = {}; - this.monitorStatus = 'unknown'; - this.refreshInterval = null; - - this.init(); - } - - init() { - this.bindEvents(); - this.loadInitialData(); - this.startAutoRefresh(); - } - - bindEvents() { - // 监控控制按钮 - document.getElementById('start-monitor').addEventListener('click', () => this.startMonitoring()); - document.getElementById('stop-monitor').addEventListener('click', () => this.stopMonitoring()); - document.getElementById('check-alerts').addEventListener('click', () => this.checkAlerts()); - document.getElementById('refresh-alerts').addEventListener('click', () => this.loadAlerts()); - - // 规则管理 - document.getElementById('save-rule').addEventListener('click', () => this.saveRule()); - document.getElementById('update-rule').addEventListener('click', () => this.updateRule()); - - // 预警过滤和排序 - document.getElementById('alert-filter').addEventListener('change', () => this.updateAlertsDisplay()); - document.getElementById('alert-sort').addEventListener('change', () => this.updateAlertsDisplay()); - - // 自动刷新 - setInterval(() => { - this.loadHealth(); - this.loadMonitorStatus(); - }, 5000); - } - - async loadInitialData() { - await Promise.all([ - this.loadHealth(), - this.loadAlerts(), - this.loadRules(), - this.loadMonitorStatus() - ]); - } - - startAutoRefresh() { - this.refreshInterval = setInterval(() => { - this.loadAlerts(); - }, 10000); // 每10秒刷新一次预警 - } - - async loadHealth() { - try { - const response = await fetch('/api/health'); - const data = await response.json(); - this.health = data; - this.updateHealthDisplay(); - } catch (error) { - console.error('加载健康状态失败:', error); - } - } - - async loadAlerts() { - try { - const response = await fetch('/api/alerts'); - const data = await response.json(); - this.alerts = data; - this.updateAlertsDisplay(); - this.updateAlertStatistics(); - } catch (error) { - console.error('加载预警失败:', error); - } - } - - async loadRules() { - try { - const response = await fetch('/api/rules'); - const data = await response.json(); - this.rules = data; - this.updateRulesDisplay(); - } catch (error) { - console.error('加载规则失败:', error); - } - } - - async loadMonitorStatus() { - try { - const response = await fetch('/api/monitor/status'); - const data = await response.json(); - this.monitorStatus = data.monitor_status; - this.updateMonitorStatusDisplay(); - } catch (error) { - console.error('加载监控状态失败:', error); - } - } - - updateHealthDisplay() { - const healthScore = this.health.health_score || 0; - const healthStatus = this.health.status || 'unknown'; - - const scoreElement = document.getElementById('health-score-text'); - const circleElement = document.getElementById('health-score-circle'); - const statusElement = document.getElementById('health-status'); - - if (scoreElement) scoreElement.textContent = Math.round(healthScore); - if (statusElement) statusElement.textContent = this.getHealthStatusText(healthStatus); - - if (circleElement) { - circleElement.className = `score-circle ${healthStatus}`; - } - } - - updateAlertsDisplay() { - const container = document.getElementById('alerts-container'); - - if (this.alerts.length === 0) { - container.innerHTML = ` -
- -
暂无活跃预警
-

系统运行正常,没有需要处理的预警

-
- `; - return; - } - - // 应用过滤和排序 - let filteredAlerts = this.filterAndSortAlerts(this.alerts); - - const alertsHtml = filteredAlerts.map(alert => { - const dataStr = alert.data ? JSON.stringify(alert.data, null, 2) : '无数据'; - return ` -
-
-
-
-
- ${this.getLevelText(alert.level)} - ${alert.rule_name || '未知规则'} - ${this.formatTime(alert.created_at)} -
-
${alert.message}
-
- 类型: ${this.getTypeText(alert.alert_type)} | - 级别: ${this.getLevelText(alert.level)} -
-
${dataStr}
-
-
- -
-
-
-
- `; - }).join(''); - - container.innerHTML = alertsHtml; - } - - updateRulesDisplay() { - const tbody = document.getElementById('rules-table'); - - if (this.rules.length === 0) { - tbody.innerHTML = ` - - 暂无规则 - - `; - return; - } - - const rulesHtml = this.rules.map(rule => ` - - ${rule.name} - ${this.getTypeText(rule.alert_type)} - ${this.getLevelText(rule.level)} - ${rule.threshold} - ${rule.enabled ? '启用' : '禁用'} - - - - - - `).join(''); - - tbody.innerHTML = rulesHtml; - } - - updateAlertStatistics() { - const stats = this.alerts.reduce((acc, alert) => { - acc[alert.level] = (acc[alert.level] || 0) + 1; - acc.total = (acc.total || 0) + 1; - return acc; - }, {}); - - document.getElementById('critical-alerts').textContent = stats.critical || 0; - document.getElementById('warning-alerts').textContent = stats.warning || 0; - document.getElementById('info-alerts').textContent = stats.info || 0; - document.getElementById('total-alerts').textContent = stats.total || 0; - } - - updateMonitorStatusDisplay() { - const statusElement = document.getElementById('monitor-status'); - const icon = statusElement.querySelector('i'); - const text = statusElement.querySelector('span') || statusElement; - - let statusText = ''; - let statusClass = ''; - - switch (this.monitorStatus) { - case 'running': - statusText = '监控运行中'; - statusClass = 'text-success'; - icon.className = 'fas fa-circle text-success'; - break; - case 'stopped': - statusText = '监控已停止'; - statusClass = 'text-danger'; - icon.className = 'fas fa-circle text-danger'; - break; - default: - statusText = '监控状态未知'; - statusClass = 'text-warning'; - icon.className = 'fas fa-circle text-warning'; - } - - if (text.textContent) { - text.textContent = statusText; - } else { - statusElement.innerHTML = ` ${statusText}`; - } - } - - async startMonitoring() { - try { - const response = await fetch('/api/monitor/start', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('监控服务已启动', 'success'); - this.loadMonitorStatus(); - } else { - this.showNotification(data.message || '启动监控失败', 'error'); - } - } catch (error) { - console.error('启动监控失败:', error); - this.showNotification('启动监控失败', 'error'); - } - } - - async stopMonitoring() { - try { - const response = await fetch('/api/monitor/stop', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('监控服务已停止', 'success'); - this.loadMonitorStatus(); - } else { - this.showNotification(data.message || '停止监控失败', 'error'); - } - } catch (error) { - console.error('停止监控失败:', error); - this.showNotification('停止监控失败', 'error'); - } - } - - async checkAlerts() { - try { - const response = await fetch('/api/check-alerts', { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification(`检查完成,发现 ${data.count} 个预警`, 'info'); - this.loadAlerts(); - } else { - this.showNotification('检查预警失败', 'error'); - } - } catch (error) { - console.error('检查预警失败:', error); - this.showNotification('检查预警失败', 'error'); - } - } - - async resolveAlert(alertId) { - try { - const response = await fetch(`/api/alerts/${alertId}/resolve`, { method: 'POST' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('预警已解决', 'success'); - this.loadAlerts(); - } else { - this.showNotification(data.message || '解决预警失败', 'error'); - } - } catch (error) { - console.error('解决预警失败:', error); - this.showNotification('解决预警失败', 'error'); - } - } - - async saveRule() { - const formData = { - name: document.getElementById('rule-name').value, - description: document.getElementById('rule-description').value, - alert_type: document.getElementById('rule-type').value, - level: document.getElementById('rule-level').value, - threshold: parseFloat(document.getElementById('rule-threshold').value), - condition: document.getElementById('rule-condition').value, - enabled: document.getElementById('rule-enabled').checked, - check_interval: parseInt(document.getElementById('rule-interval').value), - cooldown: parseInt(document.getElementById('rule-cooldown').value) - }; - - try { - const response = await fetch('/api/rules', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(formData) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('规则创建成功', 'success'); - this.hideModal('ruleModal'); - this.loadRules(); - this.resetRuleForm(); - } else { - this.showNotification(data.message || '创建规则失败', 'error'); - } - } catch (error) { - console.error('创建规则失败:', error); - this.showNotification('创建规则失败', 'error'); - } - } - - async deleteRule(ruleName) { - if (!confirm(`确定要删除规则 "${ruleName}" 吗?`)) { - return; - } - - try { - const response = await fetch(`/api/rules/${ruleName}`, { method: 'DELETE' }); - const data = await response.json(); - - if (data.success) { - this.showNotification('规则删除成功', 'success'); - this.loadRules(); - } else { - this.showNotification(data.message || '删除规则失败', 'error'); - } - } catch (error) { - console.error('删除规则失败:', error); - this.showNotification('删除规则失败', 'error'); - } - } - - filterAndSortAlerts(alerts) { - // 应用过滤 - const filter = document.getElementById('alert-filter').value; - let filtered = alerts; - - if (filter !== 'all') { - filtered = alerts.filter(alert => alert.level === filter); - } - - // 应用排序 - const sort = document.getElementById('alert-sort').value; - filtered.sort((a, b) => { - switch (sort) { - case 'time-desc': - return new Date(b.created_at) - new Date(a.created_at); - case 'time-asc': - return new Date(a.created_at) - new Date(b.created_at); - 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; - } - - editRule(ruleName) { - // 查找规则数据 - const rule = this.rules.find(r => r.name === ruleName); - if (!rule) { - this.showNotification('规则不存在', 'error'); - return; - } - - // 填充编辑表单 - document.getElementById('edit-rule-name-original').value = rule.name; - document.getElementById('edit-rule-name').value = rule.name; - document.getElementById('edit-rule-type').value = rule.alert_type; - document.getElementById('edit-rule-level').value = rule.level; - document.getElementById('edit-rule-threshold').value = rule.threshold; - document.getElementById('edit-rule-description').value = rule.description || ''; - document.getElementById('edit-rule-condition').value = rule.condition; - document.getElementById('edit-rule-interval').value = rule.check_interval; - document.getElementById('edit-rule-cooldown').value = rule.cooldown; - document.getElementById('edit-rule-enabled').checked = rule.enabled; - - // 显示编辑模态框 - const modal = new bootstrap.Modal(document.getElementById('editRuleModal')); - modal.show(); - } - - async updateRule() { - const originalName = document.getElementById('edit-rule-name-original').value; - const formData = { - name: document.getElementById('edit-rule-name').value, - description: document.getElementById('edit-rule-description').value, - alert_type: document.getElementById('edit-rule-type').value, - level: document.getElementById('edit-rule-level').value, - threshold: parseFloat(document.getElementById('edit-rule-threshold').value), - condition: document.getElementById('edit-rule-condition').value, - enabled: document.getElementById('edit-rule-enabled').checked, - check_interval: parseInt(document.getElementById('edit-rule-interval').value), - cooldown: parseInt(document.getElementById('edit-rule-cooldown').value) - }; - - try { - const response = await fetch(`/api/rules/${originalName}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(formData) - }); - - const data = await response.json(); - - if (data.success) { - this.showNotification('规则更新成功', 'success'); - this.hideModal('editRuleModal'); - this.loadRules(); - } else { - this.showNotification(data.message || '更新规则失败', 'error'); - } - } catch (error) { - console.error('更新规则失败:', error); - this.showNotification('更新规则失败', 'error'); - } - } - - resetRuleForm() { - document.getElementById('rule-form').reset(); - document.getElementById('rule-interval').value = '300'; - document.getElementById('rule-cooldown').value = '3600'; - document.getElementById('rule-enabled').checked = true; - } - - hideModal(modalId) { - const modal = document.getElementById(modalId); - const bsModal = bootstrap.Modal.getInstance(modal); - if (bsModal) { - bsModal.hide(); - } - } - - showNotification(message, type = 'info') { - // 创建通知元素 - const notification = document.createElement('div'); - notification.className = `alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show position-fixed`; - notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; - notification.innerHTML = ` - ${message} - - `; - - document.body.appendChild(notification); - - // 3秒后自动移除 - setTimeout(() => { - if (notification.parentNode) { - notification.parentNode.removeChild(notification); - } - }, 3000); - } - - getLevelText(level) { - const levelMap = { - 'critical': '严重', - 'error': '错误', - 'warning': '警告', - 'info': '信息' - }; - return levelMap[level] || level; - } - - getTypeText(type) { - const typeMap = { - 'performance': '性能', - 'quality': '质量', - 'volume': '量级', - 'system': '系统', - 'business': '业务' - }; - return typeMap[type] || type; - } - - getHealthStatusText(status) { - const statusMap = { - 'excellent': '优秀', - 'good': '良好', - 'fair': '一般', - 'poor': '较差', - 'critical': '严重', - 'unknown': '未知' - }; - return statusMap[status] || status; - } - - formatTime(timestamp) { - const date = new Date(timestamp); - const now = new Date(); - const diff = now - date; - - if (diff < 60000) { // 1分钟内 - return '刚刚'; - } else if (diff < 3600000) { // 1小时内 - return `${Math.floor(diff / 60000)}分钟前`; - } else if (diff < 86400000) { // 1天内 - return `${Math.floor(diff / 3600000)}小时前`; - } else { - return date.toLocaleDateString(); - } - } -} - -// 初始化应用 -let alertManager; -document.addEventListener('DOMContentLoaded', () => { - alertManager = new AlertManager(); -}); diff --git a/src/web/static/js/chat.js b/src/web/static/js/chat.js deleted file mode 100644 index e1d1fe8..0000000 --- a/src/web/static/js/chat.js +++ /dev/null @@ -1,410 +0,0 @@ -// 实时对话前端脚本 - -class ChatClient { - constructor() { - this.websocket = null; - this.sessionId = null; - this.isConnected = false; - this.messageCount = 0; - - this.init(); - } - - init() { - this.bindEvents(); - this.updateConnectionStatus(false); - } - - bindEvents() { - // 开始对话 - document.getElementById('start-chat').addEventListener('click', () => this.startChat()); - - // 结束对话 - document.getElementById('end-chat').addEventListener('click', () => this.endChat()); - - // 发送消息 - document.getElementById('send-button').addEventListener('click', () => this.sendMessage()); - - // 回车发送 - document.getElementById('message-input').addEventListener('keypress', (e) => { - if (e.key === 'Enter') { - this.sendMessage(); - } - }); - - // 创建工单 - document.getElementById('create-work-order').addEventListener('click', () => this.showWorkOrderModal()); - document.getElementById('create-work-order-btn').addEventListener('click', () => this.createWorkOrder()); - - // 快速操作按钮 - document.querySelectorAll('.quick-action-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - const message = e.target.getAttribute('data-message'); - document.getElementById('message-input').value = message; - this.sendMessage(); - }); - }); - } - - async startChat() { - try { - // 连接WebSocket - await this.connectWebSocket(); - - // 创建会话 - const userId = document.getElementById('user-id').value || 'anonymous'; - const workOrderId = document.getElementById('work-order-id').value || null; - - const response = await this.sendWebSocketMessage({ - type: 'create_session', - user_id: userId, - work_order_id: workOrderId ? parseInt(workOrderId) : null - }); - - if (response.type === 'session_created') { - this.sessionId = response.session_id; - this.updateSessionInfo(); - this.enableChat(); - this.addSystemMessage('对话已开始,请描述您的问题。'); - } else { - this.showError('创建会话失败'); - } - - } catch (error) { - console.error('启动对话失败:', error); - this.showError('启动对话失败: ' + error.message); - } - } - - async endChat() { - try { - if (this.sessionId) { - await this.sendWebSocketMessage({ - type: 'end_session', - session_id: this.sessionId - }); - } - - this.sessionId = null; - this.disableChat(); - this.addSystemMessage('对话已结束。'); - - } catch (error) { - console.error('结束对话失败:', error); - } - } - - async sendMessage() { - const input = document.getElementById('message-input'); - const message = input.value.trim(); - - if (!message || !this.sessionId) { - return; - } - - // 清空输入框 - input.value = ''; - - // 添加用户消息 - this.addMessage('user', message); - - // 显示打字指示器 - this.showTypingIndicator(); - - try { - const response = await this.sendWebSocketMessage({ - type: 'send_message', - session_id: this.sessionId, - message: message - }); - - this.hideTypingIndicator(); - - if (response.type === 'message_response' && response.result.success) { - const result = response.result; - - // 添加助手回复 - this.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) { - document.getElementById('work-order-id').value = result.work_order_id; - } - - } else { - this.addMessage('assistant', '抱歉,我暂时无法处理您的问题。请稍后再试。'); - } - - } catch (error) { - this.hideTypingIndicator(); - console.error('发送消息失败:', error); - this.addMessage('assistant', '发送消息失败,请检查网络连接。'); - } - } - - async createWorkOrder() { - const title = document.getElementById('wo-title').value; - const description = document.getElementById('wo-description').value; - const category = document.getElementById('wo-category').value; - const priority = document.getElementById('wo-priority').value; - - if (!title || !description) { - this.showError('请填写工单标题和描述'); - return; - } - - try { - const response = await this.sendWebSocketMessage({ - type: 'create_work_order', - session_id: this.sessionId, - title: title, - description: description, - category: category, - priority: priority - }); - - if (response.type === 'work_order_created' && response.result.success) { - const workOrderId = response.result.work_order_id; - document.getElementById('work-order-id').value = workOrderId; - this.addSystemMessage(`工单创建成功!工单号: ${response.result.order_id}`); - - // 关闭模态框 - const modal = bootstrap.Modal.getInstance(document.getElementById('workOrderModal')); - modal.hide(); - - // 清空表单 - document.getElementById('work-order-form').reset(); - - } else { - this.showError('创建工单失败: ' + (response.result.error || '未知错误')); - } - - } catch (error) { - console.error('创建工单失败:', error); - this.showError('创建工单失败: ' + error.message); - } - } - - connectWebSocket() { - return new Promise((resolve, reject) => { - try { - this.websocket = new WebSocket('ws://localhost:8765'); - - // 设置连接超时 - const timeout = setTimeout(() => { - if (this.websocket.readyState !== WebSocket.OPEN) { - this.websocket.close(); - reject(new Error('WebSocket连接超时,请检查服务器是否启动')); - } - }, 5000); // 5秒超时 - - this.websocket.onopen = () => { - clearTimeout(timeout); - this.isConnected = true; - this.updateConnectionStatus(true); - resolve(); - }; - - this.websocket.onclose = () => { - clearTimeout(timeout); - this.isConnected = false; - this.updateConnectionStatus(false); - }; - - this.websocket.onerror = (error) => { - clearTimeout(timeout); - console.error('WebSocket错误:', error); - reject(new Error('WebSocket连接失败,请检查服务器是否启动')); - }; - - this.websocket.onmessage = (event) => { - try { - const data = JSON.parse(event.data); - this.handleWebSocketMessage(data); - } catch (error) { - console.error('解析WebSocket消息失败:', error); - } - }; - - } catch (error) { - reject(error); - } - }); - } - - sendWebSocketMessage(message) { - return new Promise((resolve, reject) => { - if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) { - reject(new Error('WebSocket未连接')); - return; - } - - const messageId = 'msg_' + Date.now(); - message.messageId = messageId; - - // 设置超时 - const timeout = setTimeout(() => { - reject(new Error('请求超时')); - }, 10000); - - // 监听响应 - const handleResponse = (event) => { - try { - const data = JSON.parse(event.data); - if (data.messageId === messageId) { - clearTimeout(timeout); - this.websocket.removeEventListener('message', handleResponse); - resolve(data); - } - } catch (error) { - // 忽略解析错误 - } - }; - - this.websocket.addEventListener('message', handleResponse); - this.websocket.send(JSON.stringify(message)); - }); - } - - handleWebSocketMessage(data) { - // 处理WebSocket消息 - console.log('收到WebSocket消息:', data); - } - - addMessage(role, content, metadata = {}) { - const messagesContainer = document.getElementById('chat-messages'); - - // 如果是第一条消息,清空欢迎信息 - if (this.messageCount === 0) { - messagesContainer.innerHTML = ''; - } - - const messageDiv = document.createElement('div'); - messageDiv.className = `message ${role}`; - - const avatar = document.createElement('div'); - avatar.className = 'message-avatar'; - avatar.textContent = role === 'user' ? 'U' : 'A'; - - const contentDiv = document.createElement('div'); - contentDiv.className = 'message-content'; - contentDiv.innerHTML = content; - - // 添加时间戳 - const timeDiv = document.createElement('div'); - timeDiv.className = 'message-time'; - timeDiv.textContent = new Date().toLocaleTimeString(); - contentDiv.appendChild(timeDiv); - - // 添加元数据 - if (metadata.knowledge_used && metadata.knowledge_used.length > 0) { - const knowledgeDiv = document.createElement('div'); - knowledgeDiv.className = 'knowledge-info'; - knowledgeDiv.innerHTML = `基于 ${metadata.knowledge_used.length} 条知识库信息生成`; - contentDiv.appendChild(knowledgeDiv); - } - - if (metadata.confidence_score) { - const confidenceDiv = document.createElement('div'); - confidenceDiv.className = 'confidence-score'; - confidenceDiv.textContent = `置信度: ${(metadata.confidence_score * 100).toFixed(1)}%`; - contentDiv.appendChild(confidenceDiv); - } - - if (metadata.work_order_id) { - const workOrderDiv = document.createElement('div'); - workOrderDiv.className = 'work-order-info'; - workOrderDiv.innerHTML = `关联工单: ${metadata.work_order_id}`; - contentDiv.appendChild(workOrderDiv); - } - - if (role === 'user') { - messageDiv.appendChild(contentDiv); - messageDiv.appendChild(avatar); - } else { - messageDiv.appendChild(avatar); - messageDiv.appendChild(contentDiv); - } - - messagesContainer.appendChild(messageDiv); - messagesContainer.scrollTop = messagesContainer.scrollHeight; - - this.messageCount++; - } - - addSystemMessage(content) { - const messagesContainer = document.getElementById('chat-messages'); - - const messageDiv = document.createElement('div'); - messageDiv.className = 'text-center text-muted py-2'; - messageDiv.innerHTML = `${content}`; - - messagesContainer.appendChild(messageDiv); - messagesContainer.scrollTop = messagesContainer.scrollHeight; - } - - showTypingIndicator() { - document.getElementById('typing-indicator').classList.add('show'); - } - - hideTypingIndicator() { - document.getElementById('typing-indicator').classList.remove('show'); - } - - updateConnectionStatus(connected) { - const statusElement = document.getElementById('connection-status'); - if (connected) { - statusElement.className = 'connection-status connected'; - statusElement.innerHTML = '已连接'; - } else { - statusElement.className = 'connection-status disconnected'; - statusElement.innerHTML = '未连接'; - } - } - - updateSessionInfo() { - const sessionInfo = document.getElementById('session-info'); - sessionInfo.innerHTML = ` -
会话ID: ${this.sessionId}
-
消息数: ${this.messageCount}
-
状态: 活跃
- `; - } - - enableChat() { - document.getElementById('start-chat').disabled = true; - document.getElementById('end-chat').disabled = false; - document.getElementById('message-input').disabled = false; - document.getElementById('send-button').disabled = false; - } - - disableChat() { - document.getElementById('start-chat').disabled = false; - document.getElementById('end-chat').disabled = true; - document.getElementById('message-input').disabled = true; - document.getElementById('send-button').disabled = true; - } - - showWorkOrderModal() { - if (!this.sessionId) { - this.showError('请先开始对话'); - return; - } - - const modal = new bootstrap.Modal(document.getElementById('workOrderModal')); - modal.show(); - } - - showError(message) { - this.addSystemMessage(`错误: ${message}`); - } -} - -// 初始化聊天客户端 -document.addEventListener('DOMContentLoaded', () => { - window.chatClient = new ChatClient(); -}); diff --git a/src/web/static/js/chat_http.js b/src/web/static/js/chat_http.js deleted file mode 100644 index b8c46e5..0000000 --- a/src/web/static/js/chat_http.js +++ /dev/null @@ -1,382 +0,0 @@ -// HTTP版本实时对话前端脚本 - -class ChatHttpClient { - constructor() { - this.sessionId = null; - this.messageCount = 0; - this.apiBase = '/api/chat'; - - this.init(); - } - - init() { - this.bindEvents(); - this.updateConnectionStatus(true); - } - - bindEvents() { - // 开始对话 - document.getElementById('start-chat').addEventListener('click', () => this.startChat()); - - // 结束对话 - document.getElementById('end-chat').addEventListener('click', () => this.endChat()); - - // 发送消息 - document.getElementById('send-button').addEventListener('click', () => this.sendMessage()); - - // 回车发送 - document.getElementById('message-input').addEventListener('keypress', (e) => { - if (e.key === 'Enter') { - this.sendMessage(); - } - }); - - // 创建工单 - document.getElementById('create-work-order').addEventListener('click', () => this.showWorkOrderModal()); - document.getElementById('create-work-order-btn').addEventListener('click', () => this.createWorkOrder()); - - // 快速操作按钮 - document.querySelectorAll('.quick-action-btn').forEach(btn => { - btn.addEventListener('click', (e) => { - const message = e.target.getAttribute('data-message'); - document.getElementById('message-input').value = message; - this.sendMessage(); - }); - }); - } - - async startChat() { - try { - // 创建会话 - const userId = document.getElementById('user-id').value || 'anonymous'; - const workOrderId = document.getElementById('work-order-id').value || null; - - const response = await this.sendRequest('POST', '/session', { - user_id: userId, - work_order_id: workOrderId ? parseInt(workOrderId) : null - }); - - if (response.success) { - this.sessionId = response.session_id; - this.updateSessionInfo(); - this.enableChat(); - this.addSystemMessage('对话已开始,请描述您的问题。'); - } else { - this.showError('创建会话失败'); - } - - } catch (error) { - console.error('启动对话失败:', error); - this.showError('启动对话失败: ' + error.message); - } - } - - async endChat() { - try { - if (this.sessionId) { - await this.sendRequest('DELETE', `/session/${this.sessionId}`); - } - - this.sessionId = null; - this.disableChat(); - this.addSystemMessage('对话已结束。'); - - } catch (error) { - console.error('结束对话失败:', error); - } - } - - async sendMessage() { - const input = document.getElementById('message-input'); - const message = input.value.trim(); - - if (!message || !this.sessionId) { - return; - } - - // 清空输入框 - input.value = ''; - - // 添加用户消息 - this.addMessage('user', message); - - // 显示打字指示器 - this.showTypingIndicator(); - - try { - // 使用流式接口 - const response = await fetch('/api/chat/message/stream', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ session_id: this.sessionId, message: message }) - }); - - this.hideTypingIndicator(); - - if (!response.ok) { - this.addMessage('assistant', '请求失败,请稍后再试。'); - return; - } - - // 创建一个空的助手消息容器用于流式填充 - const msgEl = this.addMessage('assistant', '', {}, true); - const contentEl = msgEl.querySelector('.message-content') || msgEl; - let fullContent = ''; - - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - let buffer = ''; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split('\n'); - buffer = lines.pop(); // 保留不完整的行 - - for (const line of lines) { - if (!line.startsWith('data: ')) continue; - const dataStr = line.slice(6).trim(); - if (dataStr === '[DONE]') continue; - - try { - const data = JSON.parse(dataStr); - if (data.chunk) { - fullContent += data.chunk; - contentEl.textContent = fullContent; - // 自动滚动 - const chatMessages = document.getElementById('chat-messages'); - if (chatMessages) chatMessages.scrollTop = chatMessages.scrollHeight; - } - if (data.done) { - // 流结束,可以拿到 confidence_score 等元数据 - if (data.confidence_score != null) { - msgEl.dataset.confidence = data.confidence_score; - } - } - if (data.error) { - fullContent += `\n[错误: ${data.error}]`; - contentEl.textContent = fullContent; - } - } catch (e) { - // 忽略解析错误 - } - } - } - - if (!fullContent) { - contentEl.textContent = '抱歉,我暂时无法处理您的问题。请稍后再试。'; - } - - } catch (error) { - this.hideTypingIndicator(); - console.error('发送消息失败:', error); - this.addMessage('assistant', '发送消息失败,请检查网络连接。'); - } - } - - async createWorkOrder() { - const title = document.getElementById('wo-title').value; - const description = document.getElementById('wo-description').value; - const category = document.getElementById('wo-category').value; - const priority = document.getElementById('wo-priority').value; - - if (!title || !description) { - this.showError('请填写工单标题和描述'); - return; - } - - try { - const response = await this.sendRequest('POST', '/work-order', { - session_id: this.sessionId, - title: title, - description: description, - category: category, - priority: priority - }); - - if (response.success) { - const workOrderId = response.work_order_id; - document.getElementById('work-order-id').value = workOrderId; - this.addSystemMessage(`工单创建成功!工单号: ${response.order_id}`); - - // 关闭模态框 - const modal = bootstrap.Modal.getInstance(document.getElementById('workOrderModal')); - modal.hide(); - - // 清空表单 - document.getElementById('work-order-form').reset(); - - } else { - this.showError('创建工单失败: ' + (response.error || '未知错误')); - } - - } catch (error) { - console.error('创建工单失败:', error); - this.showError('创建工单失败: ' + error.message); - } - } - - async sendRequest(method, endpoint, data = null) { - const url = this.apiBase + endpoint; - const options = { - method: method, - headers: { - 'Content-Type': 'application/json', - } - }; - - if (data) { - options.body = JSON.stringify(data); - } - - const response = await fetch(url, options); - - if (!response.ok) { - throw new Error(`HTTP错误: ${response.status}`); - } - - return await response.json(); - } - - addMessage(role, content, metadata = {}, streaming = false) { - const messagesContainer = document.getElementById('chat-messages'); - - // 如果是第一条消息,清空欢迎信息 - if (this.messageCount === 0) { - messagesContainer.innerHTML = ''; - } - - const messageDiv = document.createElement('div'); - messageDiv.className = `message ${role}`; - - const avatar = document.createElement('div'); - avatar.className = 'message-avatar'; - avatar.textContent = role === 'user' ? 'U' : 'A'; - - const contentDiv = document.createElement('div'); - contentDiv.className = 'message-content'; - if (!streaming) { - contentDiv.innerHTML = content; - } else { - contentDiv.textContent = content; - } - - // 添加时间戳 - const timeDiv = document.createElement('div'); - timeDiv.className = 'message-time'; - timeDiv.textContent = new Date().toLocaleTimeString(); - if (!streaming) { - contentDiv.appendChild(timeDiv); - } - - // 添加元数据 - if (metadata.knowledge_used && metadata.knowledge_used.length > 0) { - const knowledgeDiv = document.createElement('div'); - knowledgeDiv.className = 'knowledge-info'; - knowledgeDiv.innerHTML = `基于 ${metadata.knowledge_used.length} 条知识库信息生成`; - contentDiv.appendChild(knowledgeDiv); - } - - if (metadata.confidence_score) { - const confidenceDiv = document.createElement('div'); - confidenceDiv.className = 'confidence-score'; - confidenceDiv.textContent = `置信度: ${(metadata.confidence_score * 100).toFixed(1)}%`; - contentDiv.appendChild(confidenceDiv); - } - - if (metadata.work_order_id) { - const workOrderDiv = document.createElement('div'); - workOrderDiv.className = 'work-order-info'; - workOrderDiv.innerHTML = `关联工单: ${metadata.work_order_id}`; - contentDiv.appendChild(workOrderDiv); - } - - if (role === 'user') { - messageDiv.appendChild(contentDiv); - messageDiv.appendChild(avatar); - } else { - messageDiv.appendChild(avatar); - messageDiv.appendChild(contentDiv); - } - - messagesContainer.appendChild(messageDiv); - messagesContainer.scrollTop = messagesContainer.scrollHeight; - - this.messageCount++; - return messageDiv; - } - - addSystemMessage(content) { - const messagesContainer = document.getElementById('chat-messages'); - - const messageDiv = document.createElement('div'); - messageDiv.className = 'text-center text-muted py-2'; - messageDiv.innerHTML = `${content}`; - - messagesContainer.appendChild(messageDiv); - messagesContainer.scrollTop = messagesContainer.scrollHeight; - } - - showTypingIndicator() { - document.getElementById('typing-indicator').classList.add('show'); - } - - hideTypingIndicator() { - document.getElementById('typing-indicator').classList.remove('show'); - } - - updateConnectionStatus(connected) { - const statusElement = document.getElementById('connection-status'); - if (connected) { - statusElement.className = 'connection-status connected'; - statusElement.innerHTML = 'HTTP连接'; - } else { - statusElement.className = 'connection-status disconnected'; - statusElement.innerHTML = '连接断开'; - } - } - - updateSessionInfo() { - const sessionInfo = document.getElementById('session-info'); - sessionInfo.innerHTML = ` -
会话ID: ${this.sessionId}
-
消息数: ${this.messageCount}
-
状态: 活跃
- `; - } - - enableChat() { - document.getElementById('start-chat').disabled = true; - document.getElementById('end-chat').disabled = false; - document.getElementById('message-input').disabled = false; - document.getElementById('send-button').disabled = false; - } - - disableChat() { - document.getElementById('start-chat').disabled = false; - document.getElementById('end-chat').disabled = true; - document.getElementById('message-input').disabled = true; - document.getElementById('send-button').disabled = true; - } - - showWorkOrderModal() { - if (!this.sessionId) { - this.showError('请先开始对话'); - return; - } - - const modal = new bootstrap.Modal(document.getElementById('workOrderModal')); - modal.show(); - } - - showError(message) { - this.addSystemMessage(`错误: ${message}`); - } -} - -// 初始化聊天客户端 -document.addEventListener('DOMContentLoaded', () => { - window.chatClient = new ChatHttpClient(); -}); diff --git a/src/web/static/js/main.js b/src/web/static/js/main.js deleted file mode 100644 index 649b19f..0000000 --- a/src/web/static/js/main.js +++ /dev/null @@ -1,407 +0,0 @@ -/** - * 主入口文件 - */ - -import { ready, storage } from './core/utils.js'; -import store from './core/store.js'; -import router from './core/router.js'; -import { initWebSocket } from './core/websocket.js'; -import Navbar from './components/navbar.js'; -import Sidebar from './components/sidebar.js'; -import { showToast } from './components/modal.js'; - -// 应用主类 -class App { - constructor() { - this.components = {}; - this.currentRoute = null; - } - - // 初始化应用 - async init() { - try { - // 显示加载状态 - this.showLoading(); - - // 初始化路由 - router.start(); - - // 初始化UI组件 - this.initComponents(); - - // 恢复应用状态 - this.restoreAppState(); - - // 初始化WebSocket - initWebSocket(); - - // 绑定全局事件 - this.bindGlobalEvents(); - - // 注册服务工作者(PWA支持) - this.registerServiceWorker(); - - // 隐藏加载状态 - this.hideLoading(); - - console.log('App initialized successfully'); - } catch (error) { - console.error('App initialization failed:', error); - this.handleInitError(error); - } - } - - // 初始化组件 - initComponents() { - // 初始化导航栏 - const navbarContainer = document.querySelector('#navbar'); - if (navbarContainer) { - this.components.navbar = new Navbar(navbarContainer); - } - - // 初始化侧边栏 - const sidebarContainer = document.querySelector('#sidebar-container'); - if (sidebarContainer) { - this.components.sidebar = new Sidebar(sidebarContainer); - } - - // 初始化其他组件... - } - - // 恢复应用状态 - restoreAppState() { - // 恢复主题 - const savedTheme = storage.get('app.theme', 'light'); - store.commit('SET_THEME', savedTheme); - - // 恢复用户信息(如果有) - const userInfo = storage.get('userInfo'); - if (userInfo) { - store.commit('SET_USER', userInfo); - store.commit('SET_LOGIN', true); - } - - // 恢复其他设置... - } - - // 绑定全局事件 - bindGlobalEvents() { - // 监听路由变化 - router.afterEach((to) => { - this.handleRouteChange(to); - }); - - // 监听网络状态 - window.addEventListener('online', () => { - showToast('网络已连接', 'success'); - }); - - window.addEventListener('offline', () => { - showToast('网络已断开', 'warning'); - }); - - // 监听页面可见性变化 - document.addEventListener('visibilitychange', () => { - if (document.hidden) { - store.commit('SET_APP_ACTIVE', false); - } else { - store.commit('SET_APP_ACTIVE', true); - } - }); - - // 监听存储变化(多标签页同步) - window.addEventListener('storage', (e) => { - this.handleStorageChange(e); - }); - - // 监听未捕获的错误 - window.addEventListener('error', (e) => { - this.handleError(e.error); - }); - - window.addEventListener('unhandledrejection', (e) => { - this.handleError(e.reason); - }); - } - - // 处理路由变化 - async handleRouteChange(to) { - this.currentRoute = to; - - // 更新页面标题 - if (to.meta.title) { - document.title = `${to.meta.title} - TSP智能助手`; - } - - // 加载页面组件 - await this.loadPage(to); - - // 更新导航状态 - this.updateNavigation(to); - - // 滚动到顶部 - window.scrollTo(0, 0); - } - - // 加载页面组件 - async loadPage(route) { - const pageContainer = document.querySelector('#page-content'); - if (!pageContainer) return; - - // 显示加载状态 - pageContainer.innerHTML = this.createLoadingHTML(); - - try { - // 映射路由到页面文件 - const pageFile = this.getPageFile(route.name); - - // 动态导入页面组件 (添加版本号防止缓存) - const version = '1.0.2'; - const pageModule = await import(`./pages/${pageFile}.js?v=${version}`); - const PageComponent = pageModule.default; - - // 实例化页面组件 - const page = new PageComponent(pageContainer, route); - - // 保存页面实例 - this.components.currentPage = page; - - // 将页面实例暴露到全局(供内联事件使用) - if (route.name === 'alerts') { - window.alertsPage = page; - } - - } catch (error) { - console.error('Failed to load page:', error); - pageContainer.innerHTML = this.createErrorHTML(error); - } - } - - // 获取页面文件名 - getPageFile(routeName) { - const pageMap = { - 'dashboard': 'dashboard', - 'workorders': 'workorders', - 'workorder-detail': 'workorders', - 'alerts': 'alerts', - 'knowledge': 'knowledge', - 'knowledge-detail': 'knowledge', - 'chat': 'chat', - 'chat-http': 'chat', - 'monitoring': 'monitoring', - 'settings': 'settings', - 'profile': 'settings', - 'login': 'login', - 'feishu': 'feishu', - 'agent': 'agent', - 'vehicle': 'vehicle' - }; - - return pageMap[routeName] || 'dashboard'; - } - - // 更新导航状态 - updateNavigation(route) { - // 更新侧边栏激活状态 - if (this.components.sidebar) { - this.components.sidebar.updateActiveMenu(route.path); - } - - // 更新导航栏面包屑 - this.updateBreadcrumb(route); - } - - // 更新面包屑 - updateBreadcrumb(route) { - const breadcrumbContainer = document.querySelector('#breadcrumb'); - if (!breadcrumbContainer) return; - - const items = [ - { text: '首页', link: '/' } - ]; - - // 根据路由构建面包屑 - if (route.path !== '/') { - const pathSegments = route.path.split('/').filter(Boolean); - let currentPath = ''; - - pathSegments.forEach((segment, index) => { - currentPath += `/${segment}`; - const isLast = index === pathSegments.length - 1; - - // 这里可以根据路由配置获取更友好的名称 - const name = this.getPathName(segment); - - items.push({ - text: name, - link: isLast ? null : currentPath - }); - }); - } - - breadcrumbContainer.innerHTML = this.createBreadcrumbHTML(items); - } - - // 获取路径名称 - getPathName(segment) { - const names = { - workorders: '工单管理', - alerts: '预警管理', - knowledge: '知识库', - chat: '智能对话', - 'chat-http': 'HTTP对话', - monitoring: '系统监控', - settings: '系统设置' - }; - return names[segment] || segment; - } - - // 处理存储变化 - handleStorageChange(e) { - // 处理多标签页之间的状态同步 - if (e.key === 'tsp_assistant_store') { - const newState = JSON.parse(e.newValue); - store.setState(newState, false); - } - } - - // 处理错误 - handleError(error) { - console.error('Application error:', error); - - // 显示错误提示 - showToast('发生错误,请刷新页面重试', 'error'); - - // 发送错误报告(如果配置了且有report方法) - if (window.errorReporting && window.errorReporting.enabled && window.errorReporting.report) { - try { - window.errorReporting.report(error); - } catch (reportError) { - console.error('Error reporting failed:', reportError); - } - } - } - - // 处理初始化错误 - handleInitError(error) { - this.hideLoading(); - - const pageContainer = document.querySelector('#page-content'); - if (pageContainer) { - pageContainer.innerHTML = ` -
-
-
-
-
- -

应用初始化失败

-

${error.message || '未知错误'}

- -
-
-
-
-
- `; - } - } - - // 注册服务工作者 - registerServiceWorker() { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('/sw.js') - .then(registration => { - console.log('ServiceWorker registered:', registration); - }) - .catch(error => { - console.log('ServiceWorker registration failed:', error); - }); - } - } - - // 显示加载状态 - showLoading() { - const loadingHTML = ` -
-
-
-

加载中...

-
-
- `; - document.body.insertAdjacentHTML('beforeend', loadingHTML); - } - - // 隐藏加载状态 - hideLoading() { - const overlay = document.getElementById('loading-overlay'); - if (overlay) { - overlay.remove(); - } - } - - // 创建加载HTML - createLoadingHTML() { - return ` -
-
-
-

加载中...

-
-
- `; - } - - // 创建错误HTML - createErrorHTML(error) { - return ` -
-
-
-
-
- -

页面加载失败

-

${error.message || '未知错误'}

- -
-
-
-
-
- `; - } - - // 创建面包屑HTML - createBreadcrumbHTML(items) { - return ` - - `; - } -} - -// 创建应用实例 -const app = new App(); - -// DOM加载完成后初始化应用 -ready(() => { - app.init(); -}); - -// 暴露到全局(便于调试) -window.app = app; -window.store = store; -window.router = router; \ No newline at end of file diff --git a/src/web/templates/dashboard.html b/src/web/templates/dashboard.html index e30bdfa..8768a32 100644 --- a/src/web/templates/dashboard.html +++ b/src/web/templates/dashboard.html @@ -2666,17 +2666,6 @@ - - - - - - - - - - -