优化界面布局,参考CRM系统,调整字体,优化分页显示

This commit is contained in:
赵杰
2025-09-22 17:06:43 +01:00
parent eff24947e0
commit 4da97d600a
9 changed files with 1038 additions and 63 deletions

View File

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

View File

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

View File

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

View File

@@ -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
<!-- 按钮大小 -->
<button class="btn btn-primary btn-sm">小按钮</button>
<button class="btn btn-primary">普通按钮</button>
<button class="btn btn-primary btn-lg">大按钮</button>
<!-- 按钮样式 -->
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-secondary">次要按钮</button>
<button class="btn btn-outline-primary">轮廓按钮</button>
```
### 卡片
```html
<div class="card">
<div class="card-header">
<h3 class="card-title">卡片标题</h3>
</div>
<div class="card-body">
<p>卡片内容</p>
</div>
<div class="card-footer">
<button class="btn btn-primary">操作</button>
</div>
</div>
```
### 表格
```html
<table class="table">
<thead>
<tr>
<th>列标题</th>
<th>数据类型</th>
<th>状态</th>
</tr>
</thead>
<tbody>
<tr>
<td>数据</td>
<td>文本</td>
<td><span class="badge badge-success">正常</span></td>
</tr>
</tbody>
</table>
```
### 分页
```html
<nav>
<ul class="pagination">
<li class="page-item"><a class="page-link" href="#">上一页</a></li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">下一页</a></li>
</ul>
</nav>
```
## 使用规范
### 标题层级
- **页面标题**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. 更新文档和示例
## 联系方式
如有问题或建议,请联系开发团队。

View File

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

View File

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

View File

@@ -0,0 +1,212 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TSP智能助手 - 字体和布局指南</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="design-system.css" rel="stylesheet">
<link href="style.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h1 class="card-title">TSP智能助手 - 字体和布局指南</h1>
<p class="text-secondary">参考成熟CRM系统的设计标准</p>
</div>
<div class="card-body">
<!-- 字体系统 -->
<section class="mb-6">
<h2>字体系统</h2>
<div class="row">
<div class="col-md-6">
<h3>标题层级</h3>
<h1>主标题 (H1) - 36px</h1>
<h2>大标题 (H2) - 30px</h2>
<h3>中标题 (H3) - 24px</h3>
<h4>小标题 (H4) - 20px</h4>
<h5>副标题 (H5) - 18px</h5>
<h6>小副标题 (H6) - 16px</h6>
</div>
<div class="col-md-6">
<h3>文本大小</h3>
<p class="text-4xl">超大文本 (36px)</p>
<p class="text-3xl">大文本 (30px)</p>
<p class="text-2xl">中文本 (24px)</p>
<p class="text-xl">小文本 (20px)</p>
<p class="text-lg">大正文 (18px)</p>
<p class="text-base">正文 (16px)</p>
<p class="text-sm">小正文 (14px)</p>
<p class="text-xs">辅助文本 (12px)</p>
</div>
</div>
</section>
<!-- 字重系统 -->
<section class="mb-6">
<h2>字重系统</h2>
<div class="row">
<div class="col-md-6">
<p class="font-light">轻字重 (300) - 用于辅助信息</p>
<p class="font-normal">正常字重 (400) - 用于正文</p>
<p class="font-medium">中等字重 (500) - 用于强调</p>
<p class="font-semibold">半粗字重 (600) - 用于小标题</p>
<p class="font-bold">粗字重 (700) - 用于标题</p>
</div>
<div class="col-md-6">
<h3>使用场景</h3>
<ul>
<li><strong>标题</strong>:使用 font-bold (700)</li>
<li><strong>小标题</strong>:使用 font-semibold (600)</li>
<li><strong>正文</strong>:使用 font-normal (400)</li>
<li><strong>强调</strong>:使用 font-medium (500)</li>
<li><strong>辅助信息</strong>:使用 font-light (300)</li>
</ul>
</div>
</div>
</section>
<!-- 颜色系统 -->
<section class="mb-6">
<h2>文本颜色</h2>
<div class="row">
<div class="col-md-6">
<p class="text-primary">主要文本 - 用于重要内容</p>
<p class="text-secondary">次要文本 - 用于一般内容</p>
<p class="text-tertiary">第三级文本 - 用于辅助信息</p>
<p class="text-disabled">禁用文本 - 用于不可用状态</p>
</div>
<div class="col-md-6">
<h3>状态颜色</h3>
<p class="text-success">成功状态</p>
<p class="text-warning">警告状态</p>
<p class="text-error">错误状态</p>
<p class="text-info">信息状态</p>
</div>
</div>
</section>
<!-- 间距系统 -->
<section class="mb-6">
<h2>间距系统</h2>
<div class="row">
<div class="col-md-6">
<h3>垂直间距</h3>
<div class="mb-1" style="background: #e2e8f0; padding: 4px;">mb-1 (4px)</div>
<div class="mb-2" style="background: #e2e8f0; padding: 8px;">mb-2 (8px)</div>
<div class="mb-3" style="background: #e2e8f0; padding: 12px;">mb-3 (12px)</div>
<div class="mb-4" style="background: #e2e8f0; padding: 16px;">mb-4 (16px)</div>
<div class="mb-6" style="background: #e2e8f0; padding: 24px;">mb-6 (24px)</div>
</div>
<div class="col-md-6">
<h3>水平间距</h3>
<div class="d-flex">
<div class="me-1" style="background: #e2e8f0; padding: 4px;">me-1</div>
<div class="me-2" style="background: #e2e8f0; padding: 8px;">me-2</div>
<div class="me-3" style="background: #e2e8f0; padding: 12px;">me-3</div>
</div>
</div>
</div>
</section>
<!-- 按钮系统 -->
<section class="mb-6">
<h2>按钮系统</h2>
<div class="row">
<div class="col-md-6">
<h3>按钮大小</h3>
<button class="btn btn-primary btn-sm">小按钮</button>
<button class="btn btn-primary">普通按钮</button>
<button class="btn btn-primary btn-lg">大按钮</button>
</div>
<div class="col-md-6">
<h3>按钮样式</h3>
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-secondary">次要按钮</button>
<button class="btn btn-outline-primary">轮廓按钮</button>
</div>
</div>
</section>
<!-- 表格系统 -->
<section class="mb-6">
<h2>表格系统</h2>
<table class="table">
<thead>
<tr>
<th>列标题</th>
<th>数据类型</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>数据行1</td>
<td>文本</td>
<td><span class="badge badge-success">正常</span></td>
<td><button class="btn btn-sm btn-outline-primary">编辑</button></td>
</tr>
<tr>
<td>数据行2</td>
<td>数字</td>
<td><span class="badge badge-warning">警告</span></td>
<td><button class="btn btn-sm btn-outline-primary">编辑</button></td>
</tr>
</tbody>
</table>
</section>
<!-- 分页系统 -->
<section class="mb-6">
<h2>分页系统</h2>
<nav>
<ul class="pagination">
<li class="page-item"><a class="page-link" href="#">上一页</a></li>
<li class="page-item"><a class="page-link" href="#">1</a></li>
<li class="page-item active"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item disabled"><a class="page-link" href="#">...</a></li>
<li class="page-item"><a class="page-link" href="#">10</a></li>
<li class="page-item"><a class="page-link" href="#">下一页</a></li>
</ul>
</nav>
</section>
<!-- 使用规范 -->
<section class="mb-6">
<h2>使用规范</h2>
<div class="row">
<div class="col-md-6">
<h3>标题规范</h3>
<ul>
<li>页面标题使用 H1 (36px, bold)</li>
<li>区块标题使用 H2 (30px, semibold)</li>
<li>卡片标题使用 H3 (24px, semibold)</li>
<li>表单标题使用 H4 (20px, medium)</li>
</ul>
</div>
<div class="col-md-6">
<h3>文本规范</h3>
<ul>
<li>正文使用 16px, normal</li>
<li>辅助信息使用 14px, normal</li>
<li>标签使用 12px, medium</li>
<li>按钮文字使用 14px, medium</li>
</ul>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -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 = '<div class="text-tertiary text-sm">分页数据加载中...</div>';
return;
}
if (total_pages <= 1) {
paginationContainer.innerHTML = '';
return;
@@ -365,10 +375,10 @@ class TSPDashboard {
let paginationHtml = `
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<small class="text-muted me-3">共 ${total} ${itemName},第 ${page} / ${total_pages} 页</small>
<span class="text-secondary text-sm me-3">共 ${total} ${itemName},第 ${page} / ${total_pages} 页</span>
<div class="d-flex align-items-center">
<label class="form-label me-2 mb-0">每页显示:</label>
<select class="form-select form-select-sm" style="width: auto;" onchange="dashboard.changePageSize('${containerId}', this.value, '${loadFunction}')">
<label class="form-label text-sm font-medium me-2 mb-0">每页显示:</label>
<select class="form-select form-select-sm text-sm" style="width: auto;" onchange="dashboard.changePageSize('${containerId}', this.value, '${loadFunction}')">
`;
// 每页显示条数选择器

View File

@@ -7,26 +7,9 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.css" rel="stylesheet">
<link href="{{ url_for('static', filename='css/design-system.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
<style>
:root {
--primary-color: #007bff;
--success-color: #28a745;
--warning-color: #ffc107;
--danger-color: #dc3545;
--info-color: #17a2b8;
--dark-color: #343a40;
--light-color: #f8f9fa;
}
body {
background-color: #f5f6fa;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.navbar-brand {
font-weight: bold;
font-size: 1.5rem;
}
.sidebar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);