diff --git a/src/web/blueprints/alerts.py b/src/web/blueprints/alerts.py
index 41083d8..991d4ba 100644
--- a/src/web/blueprints/alerts.py
+++ b/src/web/blueprints/alerts.py
@@ -30,14 +30,14 @@ def get_alerts():
# 构建查询
query = session.query(Alert)
- # 应用过滤器
- if level_filter:
- query = query.filter(Alert.level == level_filter)
- if status_filter:
- if status_filter == 'active':
- query = query.filter(Alert.status == 'active')
- elif status_filter == 'resolved':
- query = query.filter(Alert.status == 'resolved')
+ # 应用过滤器
+ if level_filter:
+ query = query.filter(Alert.level == level_filter)
+ if status_filter:
+ if status_filter == 'active':
+ query = query.filter(Alert.is_active == True)
+ elif status_filter == 'resolved':
+ query = query.filter(Alert.is_active == False)
# 按创建时间倒序排列
query = query.order_by(Alert.created_at.desc())
@@ -54,11 +54,12 @@ def get_alerts():
alerts_data.append({
'id': alert.id,
'rule_name': alert.rule_name,
- 'message': alert.message,
+ 'alert_type': alert.alert_type,
'level': alert.level,
- 'status': alert.status,
- 'source': alert.source,
- 'metadata': alert.metadata,
+ 'severity': alert.severity,
+ 'message': alert.message,
+ 'data': alert.data,
+ 'is_active': alert.is_active,
'created_at': alert.created_at.isoformat() if alert.created_at else None,
'resolved_at': alert.resolved_at.isoformat() if alert.resolved_at else None
})
@@ -82,7 +83,7 @@ def create_alert():
"""创建预警"""
try:
data = request.get_json()
- alert = get_assistant().create_alert(
+ alert = service_manager.get_assistant().create_alert(
alert_type=data.get('alert_type', 'manual'),
title=data.get('title', '手动预警'),
description=data.get('description', ''),
@@ -96,7 +97,7 @@ def create_alert():
def get_alert_statistics():
"""获取预警统计"""
try:
- stats = get_assistant().get_alert_statistics()
+ stats = service_manager.get_assistant().get_alert_statistics()
return jsonify(stats)
except Exception as e:
return jsonify({"error": str(e)}), 500
@@ -105,7 +106,7 @@ def get_alert_statistics():
def resolve_alert(alert_id):
"""解决预警"""
try:
- success = get_assistant().resolve_alert(alert_id)
+ success = service_manager.get_assistant().resolve_alert(alert_id)
if success:
return jsonify({"success": True, "message": "预警已解决"})
else:
diff --git a/src/web/blueprints/conversations.py b/src/web/blueprints/conversations.py
index 5393a8b..c8603be 100644
--- a/src/web/blueprints/conversations.py
+++ b/src/web/blueprints/conversations.py
@@ -38,7 +38,20 @@ def get_conversations():
for conv in result.get('conversations', []):
if 'user_id' in conv and conv['user_id'] is None:
conv.pop('user_id', None)
- return jsonify(result)
+
+ # 扁平化分页信息,与前端期望格式一致
+ if result.get('success'):
+ pagination = result.get('pagination', {})
+ return jsonify({
+ 'conversations': result.get('conversations', []),
+ 'page': pagination.get('current_page', page),
+ 'per_page': pagination.get('per_page', per_page),
+ 'total': pagination.get('total', 0),
+ 'total_pages': pagination.get('total_pages', 1),
+ 'stats': result.get('stats', {})
+ })
+ else:
+ return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
diff --git a/src/web/blueprints/workorders.py b/src/web/blueprints/workorders.py
index 8f9395c..2e6dc36 100644
--- a/src/web/blueprints/workorders.py
+++ b/src/web/blueprints/workorders.py
@@ -45,12 +45,7 @@ from src.core.query_optimizer import query_optimizer
workorders_bp = Blueprint('workorders', __name__, url_prefix='/api/workorders')
-def get_assistant():
- """获取TSP助手实例(懒加载)"""
- global _assistant
- if '_assistant' not in globals():
- _assistant = TSPAssistant()
- return _assistant
+# 移除get_assistant函数,使用service_manager
def _ensure_workorder_template_file() -> str:
"""返回已有的模板xlsx路径;不做动态生成,避免运行时依赖问题"""
@@ -142,11 +137,13 @@ def get_workorders():
'category': workorder.category,
'priority': workorder.priority,
'status': workorder.status,
- 'user_id': workorder.user_id,
- 'assigned_to': workorder.assigned_to,
+ 'assignee': workorder.assignee,
+ 'source': workorder.source,
+ 'module': workorder.module,
+ 'created_by': workorder.created_by,
'created_at': workorder.created_at.isoformat() if workorder.created_at else None,
'updated_at': workorder.updated_at.isoformat() if workorder.updated_at else None,
- 'resolved_at': workorder.resolved_at.isoformat() if workorder.resolved_at else None
+ 'date_of_close': workorder.date_of_close.isoformat() if workorder.date_of_close else None
})
# 计算分页信息
@@ -168,7 +165,7 @@ def create_workorder():
"""创建工单"""
try:
data = request.get_json()
- result = get_assistant().create_work_order(
+ result = service_manager.get_assistant().create_work_order(
title=data['title'],
description=data['description'],
category=data['category'],
@@ -304,7 +301,7 @@ def generate_workorder_ai_suggestion(workorder_id):
# 调用知识库搜索与LLM生成
# 使用问题描述(title)而不是处理过程(description)作为主要查询依据
query = f"{w.title}"
- kb_results = get_assistant().search_knowledge(query, top_k=3)
+ kb_results = service_manager.get_assistant().search_knowledge(query, top_k=3)
kb_list = kb_results.get('results', []) if isinstance(kb_results, dict) else []
# 组装提示词
context = "\n".join([f"Q: {k.get('question','')}\nA: {k.get('answer','')}" for k in kb_list])
diff --git a/src/web/static/css/README.md b/src/web/static/css/README.md
new file mode 100644
index 0000000..1b89a17
--- /dev/null
+++ b/src/web/static/css/README.md
@@ -0,0 +1,195 @@
+# TSP智能助手 - 设计系统
+
+## 概述
+
+本设计系统参考了成熟CRM系统(Salesforce、HubSpot、Zendesk)的设计标准,提供统一的字体、颜色、间距和组件规范。
+
+## 文件结构
+
+```
+src/web/static/css/
+├── design-system.css # 核心设计系统
+├── style.css # 主样式文件
+├── typography-guide.html # 字体和布局指南
+└── README.md # 本文档
+```
+
+## 字体系统
+
+### 字体族
+- **主字体**:`-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'Noto Sans', sans-serif`
+- **等宽字体**:`'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace`
+
+### 字体大小
+基于16px基准的rem系统:
+
+| 类名 | 大小 | 用途 |
+|------|------|------|
+| `.text-xs` | 12px | 辅助信息 |
+| `.text-sm` | 14px | 小文本 |
+| `.text-base` | 16px | 正文 |
+| `.text-lg` | 18px | 大文本 |
+| `.text-xl` | 20px | 小标题 |
+| `.text-2xl` | 24px | 中标题 |
+| `.text-3xl` | 30px | 大标题 |
+| `.text-4xl` | 36px | 主标题 |
+
+### 字重
+| 类名 | 字重 | 用途 |
+|------|------|------|
+| `.font-light` | 300 | 辅助信息 |
+| `.font-normal` | 400 | 正文 |
+| `.font-medium` | 500 | 强调 |
+| `.font-semibold` | 600 | 小标题 |
+| `.font-bold` | 700 | 标题 |
+
+## 颜色系统
+
+### 主色调
+- **主色**:`#2563eb` (蓝色)
+- **成功**:`#059669` (绿色)
+- **警告**:`#d97706` (橙色)
+- **错误**:`#dc2626` (红色)
+- **信息**:`#0891b2` (青色)
+
+### 文本颜色
+- **主要文本**:`#0f172a` (深灰)
+- **次要文本**:`#475569` (中灰)
+- **第三级文本**:`#64748b` (浅灰)
+- **禁用文本**:`#94a3b8` (极浅灰)
+
+## 间距系统
+
+基于8px网格的间距系统:
+
+| 类名 | 大小 | 用途 |
+|------|------|------|
+| `.spacing-1` | 4px | 最小间距 |
+| `.spacing-2` | 8px | 小间距 |
+| `.spacing-3` | 12px | 中小间距 |
+| `.spacing-4` | 16px | 中等间距 |
+| `.spacing-6` | 24px | 大间距 |
+| `.spacing-8` | 32px | 很大间距 |
+
+## 组件系统
+
+### 按钮
+```html
+
+
+
+
+
+
+
+
+
+```
+
+### 卡片
+```html
+
+```
+
+### 表格
+```html
+
+
+
+ | 列标题 |
+ 数据类型 |
+ 状态 |
+
+
+
+
+ | 数据 |
+ 文本 |
+ 正常 |
+
+
+
+```
+
+### 分页
+```html
+
+```
+
+## 使用规范
+
+### 标题层级
+- **页面标题**:H1 (36px, bold)
+- **区块标题**:H2 (30px, semibold)
+- **卡片标题**:H3 (24px, semibold)
+- **表单标题**:H4 (20px, medium)
+
+### 文本规范
+- **正文**:16px, normal
+- **辅助信息**:14px, normal
+- **标签**:12px, medium
+- **按钮文字**:14px, medium
+
+### 间距规范
+- **卡片内边距**:24px
+- **表单元素间距**:16px
+- **按钮间距**:8px
+- **文本行间距**:1.5
+
+## 响应式设计
+
+### 断点
+- **移动端**:< 768px
+- **平板端**:768px - 1024px
+- **桌面端**:> 1024px
+
+### 移动端适配
+- 按钮宽度100%
+- 表格字体缩小
+- 卡片内边距减少
+- 分页按钮适配
+
+## 浏览器支持
+
+- Chrome 90+
+- Firefox 88+
+- Safari 14+
+- Edge 90+
+
+## 更新日志
+
+### v1.0.0 (2025-09-22)
+- 初始版本
+- 基于成熟CRM系统设计标准
+- 统一的字体、颜色、间距系统
+- 完整的组件库
+- 响应式设计支持
+
+## 贡献指南
+
+1. 遵循现有的设计规范
+2. 使用CSS变量进行主题定制
+3. 保持组件的可复用性
+4. 确保响应式兼容性
+5. 更新文档和示例
+
+## 联系方式
+
+如有问题或建议,请联系开发团队。
diff --git a/src/web/static/css/design-system.css b/src/web/static/css/design-system.css
new file mode 100644
index 0000000..280dd5d
--- /dev/null
+++ b/src/web/static/css/design-system.css
@@ -0,0 +1,554 @@
+/* TSP智能助手 - 设计系统 */
+/* 参考成熟CRM系统(Salesforce、HubSpot、Zendesk)的设计标准 */
+
+/* ===== 字体系统 ===== */
+:root {
+ /* 字体族 */
+ --font-family-primary: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+ --font-family-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
+
+ /* 字体大小 - 基于16px基准的rem系统 */
+ --font-size-xs: 0.75rem; /* 12px - 辅助信息 */
+ --font-size-sm: 0.875rem; /* 14px - 小文本 */
+ --font-size-base: 1rem; /* 16px - 正文 */
+ --font-size-lg: 1.125rem; /* 18px - 大文本 */
+ --font-size-xl: 1.25rem; /* 20px - 小标题 */
+ --font-size-2xl: 1.5rem; /* 24px - 中标题 */
+ --font-size-3xl: 1.875rem; /* 30px - 大标题 */
+ --font-size-4xl: 2.25rem; /* 36px - 主标题 */
+
+ /* 行高 */
+ --line-height-tight: 1.25;
+ --line-height-normal: 1.5;
+ --line-height-relaxed: 1.75;
+
+ /* 字重 */
+ --font-weight-light: 300;
+ --font-weight-normal: 400;
+ --font-weight-medium: 500;
+ --font-weight-semibold: 600;
+ --font-weight-bold: 700;
+
+ /* 间距系统 - 基于8px网格 */
+ --spacing-1: 0.25rem; /* 4px */
+ --spacing-2: 0.5rem; /* 8px */
+ --spacing-3: 0.75rem; /* 12px */
+ --spacing-4: 1rem; /* 16px */
+ --spacing-5: 1.25rem; /* 20px */
+ --spacing-6: 1.5rem; /* 24px */
+ --spacing-8: 2rem; /* 32px */
+ --spacing-10: 2.5rem; /* 40px */
+ --spacing-12: 3rem; /* 48px */
+ --spacing-16: 4rem; /* 64px */
+
+ /* 颜色系统 */
+ --color-primary: #2563eb;
+ --color-primary-dark: #1d4ed8;
+ --color-primary-light: #3b82f6;
+ --color-secondary: #64748b;
+ --color-success: #059669;
+ --color-warning: #d97706;
+ --color-error: #dc2626;
+ --color-info: #0891b2;
+
+ /* 中性色 */
+ --color-gray-50: #f8fafc;
+ --color-gray-100: #f1f5f9;
+ --color-gray-200: #e2e8f0;
+ --color-gray-300: #cbd5e1;
+ --color-gray-400: #94a3b8;
+ --color-gray-500: #64748b;
+ --color-gray-600: #475569;
+ --color-gray-700: #334155;
+ --color-gray-800: #1e293b;
+ --color-gray-900: #0f172a;
+
+ /* 文本颜色 */
+ --text-primary: var(--color-gray-900);
+ --text-secondary: var(--color-gray-600);
+ --text-tertiary: var(--color-gray-500);
+ --text-disabled: var(--color-gray-400);
+ --text-inverse: #ffffff;
+
+ /* 背景颜色 */
+ --bg-primary: #ffffff;
+ --bg-secondary: var(--color-gray-50);
+ --bg-tertiary: var(--color-gray-100);
+ --bg-elevated: #ffffff;
+
+ /* 边框 */
+ --border-color: var(--color-gray-200);
+ --border-color-hover: var(--color-gray-300);
+ --border-radius-sm: 0.25rem;
+ --border-radius: 0.375rem;
+ --border-radius-lg: 0.5rem;
+ --border-radius-xl: 0.75rem;
+
+ /* 阴影 */
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+}
+
+/* ===== 基础字体设置 ===== */
+* {
+ box-sizing: border-box;
+}
+
+body {
+ font-family: var(--font-family-primary);
+ font-size: var(--font-size-base);
+ line-height: var(--line-height-normal);
+ color: var(--text-primary);
+ background-color: var(--bg-secondary);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+/* ===== 标题系统 ===== */
+h1, .h1 {
+ font-size: var(--font-size-4xl);
+ font-weight: var(--font-weight-bold);
+ line-height: var(--line-height-tight);
+ margin: 0 0 var(--spacing-6) 0;
+ color: var(--text-primary);
+}
+
+h2, .h2 {
+ font-size: var(--font-size-3xl);
+ font-weight: var(--font-weight-semibold);
+ line-height: var(--line-height-tight);
+ margin: 0 0 var(--spacing-5) 0;
+ color: var(--text-primary);
+}
+
+h3, .h3 {
+ font-size: var(--font-size-2xl);
+ font-weight: var(--font-weight-semibold);
+ line-height: var(--line-height-normal);
+ margin: 0 0 var(--spacing-4) 0;
+ color: var(--text-primary);
+}
+
+h4, .h4 {
+ font-size: var(--font-size-xl);
+ font-weight: var(--font-weight-medium);
+ line-height: var(--line-height-normal);
+ margin: 0 0 var(--spacing-3) 0;
+ color: var(--text-primary);
+}
+
+h5, .h5 {
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-medium);
+ line-height: var(--line-height-normal);
+ margin: 0 0 var(--spacing-2) 0;
+ color: var(--text-primary);
+}
+
+h6, .h6 {
+ font-size: var(--font-size-base);
+ font-weight: var(--font-weight-semibold);
+ line-height: var(--line-height-normal);
+ margin: 0 0 var(--spacing-2) 0;
+ color: var(--text-primary);
+}
+
+/* ===== 文本样式 ===== */
+.text-xs { font-size: var(--font-size-xs); }
+.text-sm { font-size: var(--font-size-sm); }
+.text-base { font-size: var(--font-size-base); }
+.text-lg { font-size: var(--font-size-lg); }
+.text-xl { font-size: var(--font-size-xl); }
+.text-2xl { font-size: var(--font-size-2xl); }
+.text-3xl { font-size: var(--font-size-3xl); }
+.text-4xl { font-size: var(--font-size-4xl); }
+
+.font-light { font-weight: var(--font-weight-light); }
+.font-normal { font-weight: var(--font-weight-normal); }
+.font-medium { font-weight: var(--font-weight-medium); }
+.font-semibold { font-weight: var(--font-weight-semibold); }
+.font-bold { font-weight: var(--font-weight-bold); }
+
+.text-primary { color: var(--text-primary); }
+.text-secondary { color: var(--text-secondary); }
+.text-tertiary { color: var(--text-tertiary); }
+.text-disabled { color: var(--text-disabled); }
+.text-inverse { color: var(--text-inverse); }
+
+/* ===== 布局系统 ===== */
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0 var(--spacing-4);
+}
+
+.container-fluid {
+ width: 100%;
+ padding: 0 var(--spacing-4);
+}
+
+/* 网格系统 */
+.row {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0 calc(var(--spacing-2) * -1);
+}
+
+.col {
+ flex: 1;
+ padding: 0 var(--spacing-2);
+}
+
+.col-1 { flex: 0 0 8.333333%; }
+.col-2 { flex: 0 0 16.666667%; }
+.col-3 { flex: 0 0 25%; }
+.col-4 { flex: 0 0 33.333333%; }
+.col-6 { flex: 0 0 50%; }
+.col-8 { flex: 0 0 66.666667%; }
+.col-9 { flex: 0 0 75%; }
+.col-12 { flex: 0 0 100%; }
+
+/* ===== 卡片系统 ===== */
+.card {
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-lg);
+ box-shadow: var(--shadow-sm);
+ overflow: hidden;
+ transition: box-shadow 0.2s ease;
+}
+
+.card:hover {
+ box-shadow: var(--shadow-md);
+}
+
+.card-header {
+ padding: var(--spacing-4) var(--spacing-6);
+ border-bottom: 1px solid var(--border-color);
+ background: var(--bg-secondary);
+}
+
+.card-title {
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-semibold);
+ margin: 0;
+ color: var(--text-primary);
+}
+
+.card-body {
+ padding: var(--spacing-6);
+}
+
+.card-footer {
+ padding: var(--spacing-4) var(--spacing-6);
+ border-top: 1px solid var(--border-color);
+ background: var(--bg-secondary);
+}
+
+/* ===== 按钮系统 ===== */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--spacing-2) var(--spacing-4);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ line-height: var(--line-height-tight);
+ text-decoration: none;
+ border: 1px solid transparent;
+ border-radius: var(--border-radius);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ white-space: nowrap;
+ user-select: none;
+}
+
+.btn:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.btn-sm {
+ padding: var(--spacing-1) var(--spacing-3);
+ font-size: var(--font-size-xs);
+}
+
+.btn-lg {
+ padding: var(--spacing-3) var(--spacing-6);
+ font-size: var(--font-size-base);
+}
+
+.btn-primary {
+ background-color: var(--color-primary);
+ border-color: var(--color-primary);
+ color: var(--text-inverse);
+}
+
+.btn-primary:hover:not(:disabled) {
+ background-color: var(--color-primary-dark);
+ border-color: var(--color-primary-dark);
+}
+
+.btn-secondary {
+ background-color: var(--color-secondary);
+ border-color: var(--color-secondary);
+ color: var(--text-inverse);
+}
+
+.btn-outline-primary {
+ background-color: transparent;
+ border-color: var(--color-primary);
+ color: var(--color-primary);
+}
+
+.btn-outline-primary:hover:not(:disabled) {
+ background-color: var(--color-primary);
+ color: var(--text-inverse);
+}
+
+/* ===== 表单系统 ===== */
+.form-group {
+ margin-bottom: var(--spacing-4);
+}
+
+.form-label {
+ display: block;
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ color: var(--text-primary);
+ margin-bottom: var(--spacing-1);
+}
+
+.form-control {
+ display: block;
+ width: 100%;
+ padding: var(--spacing-2) var(--spacing-3);
+ font-size: var(--font-size-sm);
+ line-height: var(--line-height-normal);
+ color: var(--text-primary);
+ background-color: var(--bg-primary);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+
+.form-control:focus {
+ outline: none;
+ border-color: var(--color-primary);
+ box-shadow: 0 0 0 3px rgb(37 99 235 / 0.1);
+}
+
+.form-select {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e");
+ background-position: right var(--spacing-2) center;
+ background-repeat: no-repeat;
+ background-size: 1.5em 1.5em;
+ padding-right: var(--spacing-8);
+}
+
+/* ===== 表格系统 ===== */
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: var(--font-size-sm);
+}
+
+.table th,
+.table td {
+ padding: var(--spacing-3) var(--spacing-4);
+ text-align: left;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.table th {
+ font-weight: var(--font-weight-semibold);
+ color: var(--text-primary);
+ background-color: var(--bg-secondary);
+}
+
+.table td {
+ color: var(--text-secondary);
+}
+
+.table tbody tr:hover {
+ background-color: var(--bg-secondary);
+}
+
+/* ===== 徽章系统 ===== */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ padding: var(--spacing-1) var(--spacing-2);
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-medium);
+ line-height: 1;
+ border-radius: var(--border-radius-sm);
+ text-transform: uppercase;
+ letter-spacing: 0.025em;
+}
+
+.badge-primary {
+ background-color: var(--color-primary);
+ color: var(--text-inverse);
+}
+
+.badge-success {
+ background-color: var(--color-success);
+ color: var(--text-inverse);
+}
+
+.badge-warning {
+ background-color: var(--color-warning);
+ color: var(--text-inverse);
+}
+
+.badge-error {
+ background-color: var(--color-error);
+ color: var(--text-inverse);
+}
+
+.badge-secondary {
+ background-color: var(--color-secondary);
+ color: var(--text-inverse);
+}
+
+/* ===== 分页系统 ===== */
+.pagination {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-1);
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+.pagination .page-item {
+ display: flex;
+}
+
+.pagination .page-link {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 2.5rem;
+ height: 2.5rem;
+ padding: var(--spacing-2);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-medium);
+ color: var(--text-secondary);
+ text-decoration: none;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ transition: all 0.2s ease;
+}
+
+.pagination .page-link:hover {
+ background-color: var(--bg-secondary);
+ border-color: var(--border-color-hover);
+ color: var(--text-primary);
+}
+
+.pagination .page-item.active .page-link {
+ background-color: var(--color-primary);
+ border-color: var(--color-primary);
+ color: var(--text-inverse);
+}
+
+.pagination .page-item.disabled .page-link {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+/* ===== 工具类 ===== */
+.d-flex { display: flex; }
+.d-block { display: block; }
+.d-inline { display: inline; }
+.d-inline-block { display: inline-block; }
+.d-none { display: none; }
+
+.justify-content-start { justify-content: flex-start; }
+.justify-content-center { justify-content: center; }
+.justify-content-end { justify-content: flex-end; }
+.justify-content-between { justify-content: space-between; }
+
+.align-items-start { align-items: flex-start; }
+.align-items-center { align-items: center; }
+.align-items-end { align-items: flex-end; }
+
+.text-left { text-align: left; }
+.text-center { text-align: center; }
+.text-right { text-align: right; }
+
+.mb-0 { margin-bottom: 0; }
+.mb-1 { margin-bottom: var(--spacing-1); }
+.mb-2 { margin-bottom: var(--spacing-2); }
+.mb-3 { margin-bottom: var(--spacing-3); }
+.mb-4 { margin-bottom: var(--spacing-4); }
+.mb-6 { margin-bottom: var(--spacing-6); }
+
+.mt-0 { margin-top: 0; }
+.mt-1 { margin-top: var(--spacing-1); }
+.mt-2 { margin-top: var(--spacing-2); }
+.mt-3 { margin-top: var(--spacing-3); }
+.mt-4 { margin-top: var(--spacing-4); }
+.mt-6 { margin-top: var(--spacing-6); }
+
+.me-1 { margin-right: var(--spacing-1); }
+.me-2 { margin-right: var(--spacing-2); }
+.me-3 { margin-right: var(--spacing-3); }
+.ms-1 { margin-left: var(--spacing-1); }
+.ms-2 { margin-left: var(--spacing-2); }
+.ms-3 { margin-left: var(--spacing-3); }
+
+.p-0 { padding: 0; }
+.p-1 { padding: var(--spacing-1); }
+.p-2 { padding: var(--spacing-2); }
+.p-3 { padding: var(--spacing-3); }
+.p-4 { padding: var(--spacing-4); }
+.p-6 { padding: var(--spacing-6); }
+
+/* ===== 响应式设计 ===== */
+@media (max-width: 768px) {
+ .container {
+ padding: 0 var(--spacing-2);
+ }
+
+ .card-body {
+ padding: var(--spacing-4);
+ }
+
+ .btn {
+ width: 100%;
+ margin-bottom: var(--spacing-2);
+ }
+
+ .table {
+ font-size: var(--font-size-xs);
+ }
+
+ .table th,
+ .table td {
+ padding: var(--spacing-2);
+ }
+}
+
+/* ===== 打印样式 ===== */
+@media print {
+ .btn,
+ .pagination,
+ .card-footer {
+ display: none;
+ }
+
+ .card {
+ border: 1px solid #000;
+ box-shadow: none;
+ }
+
+ .table th,
+ .table td {
+ border: 1px solid #000;
+ }
+}
diff --git a/src/web/static/css/style.css b/src/web/static/css/style.css
index 65f3306..061407c 100644
--- a/src/web/static/css/style.css
+++ b/src/web/static/css/style.css
@@ -1,13 +1,20 @@
-/* TSP助手预警管理系统样式 */
+/* TSP智能助手 - 主样式文件 */
+/* 引入设计系统 */
+@import url('design-system.css');
+/* 覆盖和扩展设计系统 */
body {
- background-color: #f8f9fa;
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background-color: var(--bg-secondary);
+ font-family: var(--font-family-primary);
+ font-size: var(--font-size-base);
+ line-height: var(--line-height-normal);
+ color: var(--text-primary);
}
.navbar-brand {
- font-weight: bold;
- font-size: 1.5rem;
+ font-size: var(--font-size-2xl);
+ font-weight: var(--font-weight-bold);
+ color: var(--text-primary);
}
/* 健康状态圆圈 */
@@ -22,10 +29,10 @@ body {
display: flex;
align-items: center;
justify-content: center;
- margin: 0 auto 10px;
- font-size: 1.5rem;
- font-weight: bold;
- color: white;
+ margin: 0 auto var(--spacing-2);
+ font-size: var(--font-size-2xl);
+ font-weight: var(--font-weight-bold);
+ color: var(--text-inverse);
position: relative;
}
@@ -113,15 +120,18 @@ body {
/* 规则表格 */
.table th {
- background-color: #f8f9fa;
+ background-color: var(--bg-secondary);
border-top: none;
- font-weight: 600;
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-semibold);
+ color: var(--text-primary);
}
.rule-status {
- font-size: 0.8rem;
- padding: 0.25rem 0.5rem;
- border-radius: 0.25rem;
+ font-size: var(--font-size-xs);
+ font-weight: var(--font-weight-medium);
+ padding: var(--spacing-1) var(--spacing-2);
+ border-radius: var(--border-radius-sm);
}
.rule-status.enabled {
diff --git a/src/web/static/css/typography-guide.html b/src/web/static/css/typography-guide.html
new file mode 100644
index 0000000..ed108ee
--- /dev/null
+++ b/src/web/static/css/typography-guide.html
@@ -0,0 +1,212 @@
+
+
+
+
+
+ TSP智能助手 - 字体和布局指南
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 字体系统
+
+
+
标题层级
+ 主标题 (H1) - 36px
+ 大标题 (H2) - 30px
+ 中标题 (H3) - 24px
+ 小标题 (H4) - 20px
+ 副标题 (H5) - 18px
+ 小副标题 (H6) - 16px
+
+
+
文本大小
+
超大文本 (36px)
+
大文本 (30px)
+
中文本 (24px)
+
小文本 (20px)
+
大正文 (18px)
+
正文 (16px)
+
小正文 (14px)
+
辅助文本 (12px)
+
+
+
+
+
+
+ 字重系统
+
+
+
轻字重 (300) - 用于辅助信息
+
正常字重 (400) - 用于正文
+
中等字重 (500) - 用于强调
+
半粗字重 (600) - 用于小标题
+
粗字重 (700) - 用于标题
+
+
+
使用场景
+
+ - 标题:使用 font-bold (700)
+ - 小标题:使用 font-semibold (600)
+ - 正文:使用 font-normal (400)
+ - 强调:使用 font-medium (500)
+ - 辅助信息:使用 font-light (300)
+
+
+
+
+
+
+
+ 文本颜色
+
+
+
主要文本 - 用于重要内容
+
次要文本 - 用于一般内容
+
第三级文本 - 用于辅助信息
+
禁用文本 - 用于不可用状态
+
+
+
状态颜色
+
成功状态
+
警告状态
+
错误状态
+
信息状态
+
+
+
+
+
+
+ 间距系统
+
+
+
垂直间距
+
mb-1 (4px)
+
mb-2 (8px)
+
mb-3 (12px)
+
mb-4 (16px)
+
mb-6 (24px)
+
+
+
+
+
+
+
+ 按钮系统
+
+
+
按钮大小
+
+
+
+
+
+
按钮样式
+
+
+
+
+
+
+
+
+
+ 表格系统
+
+
+
+ | 列标题 |
+ 数据类型 |
+ 状态 |
+ 操作 |
+
+
+
+
+ | 数据行1 |
+ 文本 |
+ 正常 |
+ |
+
+
+ | 数据行2 |
+ 数字 |
+ 警告 |
+ |
+
+
+
+
+
+
+
+
+
+
+ 使用规范
+
+
+
标题规范
+
+ - 页面标题使用 H1 (36px, bold)
+ - 区块标题使用 H2 (30px, semibold)
+ - 卡片标题使用 H3 (24px, semibold)
+ - 表单标题使用 H4 (20px, medium)
+
+
+
+
文本规范
+
+ - 正文使用 16px, normal
+ - 辅助信息使用 14px, normal
+ - 标签使用 12px, medium
+ - 按钮文字使用 14px, medium
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/web/static/js/dashboard.js b/src/web/static/js/dashboard.js
index 5f48995..01e9445 100644
--- a/src/web/static/js/dashboard.js
+++ b/src/web/static/js/dashboard.js
@@ -355,8 +355,18 @@ class TSPDashboard {
const paginationContainer = document.getElementById(containerId);
if (!paginationContainer) return;
+ // 调试信息
+ console.log(`分页数据 (${containerId}):`, data);
+
const { page, total_pages, total, per_page } = data;
+ // 检查必要字段
+ if (page === undefined || total_pages === undefined || total === undefined || per_page === undefined) {
+ console.error(`分页数据不完整 (${containerId}):`, { page, total_pages, total, per_page });
+ paginationContainer.innerHTML = '分页数据加载中...
';
+ return;
+ }
+
if (total_pages <= 1) {
paginationContainer.innerHTML = '';
return;
@@ -365,10 +375,10 @@ class TSPDashboard {
let paginationHtml = `
-
共 ${total} ${itemName},第 ${page} / ${total_pages} 页
+
共 ${total} ${itemName},第 ${page} / ${total_pages} 页
-
-