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 @@
-
-
-
-
-
-
-
-
-
-
-