Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions backend/app/api/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
from sqlalchemy import or_, and_
from sqlalchemy.orm import Session

from app.core.dependencies import get_db, get_current_user, get_current_community
from app.models.user import User
from app.models.content import Content
from app.models.meeting import Meeting
from sqlalchemy import func

from app.core.dependencies import get_db, get_current_user, get_current_community, get_current_active_superuser
from app.models.user import User, community_users
from app.models.content import Content, content_assignees
from app.models.meeting import Meeting, meeting_assignees
from app.schemas.dashboard import (
DashboardResponse,
AssignedItem,
WorkStatusStats,
UpdateWorkStatusRequest,
ContentByTypeStats,
UserWorkloadItem,
WorkloadOverviewResponse,
)

router = APIRouter()
Expand Down Expand Up @@ -254,6 +259,69 @@ async def update_meeting_work_status(
}


@router.get("/workload-overview", response_model=WorkloadOverviewResponse)
async def get_workload_overview(
current_user: User = Depends(get_current_active_superuser),
db: Session = Depends(get_db),
):
"""
获取所有用户的工作量总览(仅超级管理员)
统计每个用户的内容和会议数量,按状态和类别分组
"""
# 获取所有非默认管理员用户
users = (
db.query(User)
.filter(User.is_default_admin == False) # noqa: E712
.order_by(User.id)
.all()
)

result = []
for user in users:
# 获取用户负责的内容
assigned_contents = (
db.query(Content)
.join(Content.assignees)
.filter(User.id == user.id)
.all()
)

# 获取用户负责的会议
assigned_meetings = (
db.query(Meeting)
.join(Meeting.assignees)
.filter(User.id == user.id)
.all()
)

# 内容按 work_status 统计
content_stats = _calculate_work_status_stats(assigned_contents)

# 会议按 status 统计(映射到 work_status)
meeting_stats = _calculate_work_status_stats(assigned_meetings)

# 内容按 source_type 统计
type_stats = {"contribution": 0, "release_note": 0, "event_summary": 0}
for content in assigned_contents:
st = content.source_type or "contribution"
if st in type_stats:
type_stats[st] += 1

total = len(assigned_contents) + len(assigned_meetings)

result.append(UserWorkloadItem(
user_id=user.id,
username=user.username,
full_name=user.full_name,
content_stats=content_stats,
meeting_stats=meeting_stats,
content_by_type=ContentByTypeStats(**type_stats),
total=total,
))

return WorkloadOverviewResponse(users=result)


def _calculate_work_status_stats(items) -> WorkStatusStats:
"""计算工作状态统计"""
stats = {"planning": 0, "in_progress": 0, "completed": 0}
Expand Down
23 changes: 23 additions & 0 deletions backend/app/schemas/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,26 @@ class AssigneeResponse(BaseModel):
full_name: str
email: str
assigned_at: datetime


class ContentByTypeStats(BaseModel):
"""按内容类型统计"""
contribution: int = 0
release_note: int = 0
event_summary: int = 0


class UserWorkloadItem(BaseModel):
"""单个用户的工作量数据"""
user_id: int
username: str
full_name: Optional[str] = None
content_stats: WorkStatusStats
meeting_stats: WorkStatusStats
content_by_type: ContentByTypeStats
total: int


class WorkloadOverviewResponse(BaseModel):
"""工作量总览响应"""
users: List[UserWorkloadItem]
267 changes: 267 additions & 0 deletions docs/design/04-UI风格优化指南.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
# openGecko UI 风格优化指南

## 参考页面
`frontend/src/views/WorkloadOverview.vue` — 基于 LF Insights 风格设计

---

## 一、设计原则

1. **卡片化布局**:避免直接使用 `el-card`,改用自定义 `div` + CSS 实现更精致的卡片效果
2. **信息层级清晰**:页面标题 → 指标概览 → 主要内容 → 辅助信息
3. **颜色克制**:以灰色调为主,仅在关键数据和状态处使用彩色
4. **数据可视化**:用进度条、徽章、图标等替代纯数字表格

---

## 二、色彩规范

### 文字颜色
| 用途 | 色值 | 说明 |
|------|------|------|
| 主标题 | `#1d2129` | 页面标题、重要数字 |
| 正文 | `#4e5969` | 普通文本、标签名 |
| 辅助文字 | `#86909c` | 副标题、描述、图例 |
| 弱化文字 | `#a0a8b4` | 用户名后缀、次要信息 |
| 占位/空状态 | `#c0c4cc` | 无数据提示 |

### 状态颜色
| 状态 | 色值 | 用途 |
|------|------|------|
| 计划中/待处理 | `#94a3b8` | 进度条、圆点、徽章 |
| 进行中/警告 | `#f59e0b` | 进度条、指标数字 |
| 已完成/成功 | `#10b981` | 进度条、指标数字 |
| 主要/链接 | `#3b82f6` | 蓝色徽章、链接色 |
| 危险/错误 | `#ef4444` | 删除操作、错误状态 |

### 背景颜色
| 用途 | 色值 |
|------|------|
| 页面背景 | `#f5f7fa`(已有) |
| 卡片背景 | `#fff` |
| 卡片头部/表头 | `#fafbfc` |
| 进度条底色 | `#f7f8fa` |
| 排名徽章(默认) | `#f7f8fa` |

### 排名/强调色
| 等级 | 背景 | 文字色 |
|------|------|--------|
| 金(第1) | `#fef3c7` | `#d97706` |
| 银(第2) | `#f1f5f9` | `#64748b` |
| 铜(第3) | `#fef2e8` | `#c2410c` |

### 分类徽章
| 类型 | 背景 | 文字色 |
|------|------|--------|
| 内容相关 | `#eff6ff` | `#3b82f6` |
| 会议相关 | `#f0fdf4` | `#22c55e` |

---

## 三、排版规范

### 字号
| 元素 | 大小 | 粗细 |
|------|------|------|
| 页面标题 h2 | `22px` | `600` |
| 副标题/描述 | `14px` | `400` |
| 区块标题 h3 | `16px` | `600` |
| 指标大数字 | `32px` | `700` |
| 正文 | `14px` | `400/500` |
| 辅助标签 | `13px` | `400/500` |
| 小字/标签 | `12px` | `500` |

### 间距
| 元素 | 间距 |
|------|------|
| 页面标题下方 | `24px` |
| 指标卡片网格间距 | `16px` |
| 指标卡片与下方区块 | `28px` |
| 区块之间 | `20px` |
| 区块内标题到内容 | `20px` |
| 列表项上下内边距 | `12px` |

---

## 四、组件模式

### 4.1 页面标题
```html
<div class="page-title">
<h2>页面名称</h2>
<p class="subtitle">页面描述说明</p>
</div>
```
```css
.page-title { margin-bottom: 24px; }
.page-title h2 { margin: 0 0 4px; font-size: 22px; font-weight: 600; color: #1d2129; }
.page-title .subtitle { margin: 0; color: #86909c; font-size: 14px; }
```

### 4.2 指标卡片网格
```html
<div class="metric-cards">
<div class="metric-card">
<div class="metric-value">42</div>
<div class="metric-label">标签</div>
</div>
<!-- 可加 highlight-warning / highlight-success 类 -->
</div>
```
```css
.metric-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
.metric-card { background: #fff; border-radius: 12px; padding: 20px 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); border: 1px solid #f0f0f0; }
.metric-value { font-size: 32px; font-weight: 700; color: #1d2129; line-height: 1.2; }
.metric-label { font-size: 13px; color: #86909c; margin-top: 4px; }
.metric-card.highlight-warning .metric-value { color: #f59e0b; }
.metric-card.highlight-success .metric-value { color: #10b981; }
```

### 4.3 内容区块(白色卡片容器)
```html
<div class="section-card">
<div class="section-header">
<h3>区块标题</h3>
<span class="section-desc">说明文字</span>
</div>
<!-- 内容 -->
</div>
```
```css
.section-card { background: #fff; border-radius: 12px; padding: 24px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); border: 1px solid #f0f0f0; }
.section-header { display: flex; align-items: baseline; gap: 12px; margin-bottom: 20px; }
.section-header h3 { margin: 0; font-size: 16px; font-weight: 600; color: #1d2129; }
.section-desc { font-size: 13px; color: #86909c; }
```

### 4.4 列表项
```html
<div class="list-item">
<div class="user-avatar">Z</div>
<div class="item-content">
<span class="item-title">标题</span>
<span class="item-meta">元信息</span>
</div>
<div class="item-actions">...</div>
</div>
```
```css
.list-item { display: flex; align-items: center; gap: 16px; padding: 12px 0; border-bottom: 1px solid #f5f5f5; }
.list-item:last-child { border-bottom: none; }
```

### 4.5 用户头像
```css
.user-avatar { width: 36px; height: 36px; border-radius: 50%; background: linear-gradient(135deg, #667eea, #764ba2); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 600; flex-shrink: 0; }
.user-avatar.small { width: 32px; height: 32px; font-size: 13px; }
```

### 4.6 状态圆点
```css
.stat-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.stat-dot.planning { background: #94a3b8; }
.stat-dot.in-progress { background: #f59e0b; }
.stat-dot.completed { background: #10b981; }
```

### 4.7 堆叠进度条
```html
<div class="task-bar">
<div class="bar-segment planning" :style="{ width: '30%' }" />
<div class="bar-segment in-progress" :style="{ width: '40%' }" />
<div class="bar-segment completed" :style="{ width: '30%' }" />
</div>
```
```css
.task-bar { display: flex; height: 20px; border-radius: 4px; overflow: hidden; background: #f7f8fa; }
.bar-segment { height: 100%; transition: width 0.4s ease; }
.bar-segment.planning { background: #94a3b8; }
.bar-segment.in-progress { background: #f59e0b; }
.bar-segment.completed { background: #10b981; }
```

### 4.8 分类徽章
```css
.count-badge { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; padding: 2px 8px; border-radius: 10px; font-weight: 500; }
.content-badge { background: #eff6ff; color: #3b82f6; }
.meeting-badge { background: #f0fdf4; color: #22c55e; }
```

### 4.9 详情卡片网格
```css
.detail-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
.detail-card { border: 1px solid #f0f0f0; border-radius: 10px; overflow: hidden; }
.detail-card-header { display: flex; align-items: center; gap: 10px; padding: 14px 16px; background: #fafbfc; border-bottom: 1px solid #f0f0f0; }
.detail-card-body { padding: 16px; display: flex; flex-direction: column; gap: 14px; }
```

---

## 五、各页面适配要点

### Dashboard.vue
- 用 `metric-cards` 网格替换 `el-row` + `el-col` 统计卡片
- 用 `section-card` 替换 `el-card`
- 频道统计和最近内容用 `list-item` 模式

### MyWork.vue
- 标题区用 `page-title` + `metric-cards` 替换当前 header-card 布局
- 筛选栏放在 `section-card` 内
- 工作项列表用 `list-item` 模式,保留堆叠进度条

### ContentList.vue
- `page-header` 改为 `page-title` + 右侧操作按钮(用 flex justify-content: space-between)
- 筛选区放在 `section-card` 内
- 表格外包 `section-card`

### CommunityOverview.vue
- 统计卡片改为 `metric-cards` 网格
- 社区表格外包 `section-card`

### CommitteeList.vue
- 用 `page-title` 替换 `page-header`
- 委员会卡片网格改用 `detail-grid` 模式

### GovernanceOverview.vue
- 统计卡片改为 `metric-cards` 网格
- 委员会列表和会议列表各用 `section-card`
- 列表项用 `list-item` 模式

### UserManage.vue
- 用 `page-title` + 按钮替换 `page-header`
- 表格外包 `section-card`

### MeetingCalendar.vue
- 筛选栏和日历各用 `section-card`
- 列表视图的会议项用 `list-item` 模式

### ContentCalendar.vue
- 主体区域用 `section-card`
- 保留现有 FullCalendar 集成,仅调整外部容器

### PublishView.vue
- 左右两栏各用 `section-card`

### Settings.vue
- 各频道配置用 `detail-card` 模式

### ContentEdit.vue
- 表单元数据区用 `section-card`
- 编辑器区域保持现有布局

### CommitteeDetail.vue / MeetingDetail.vue
- 信息卡片和元数据用 `section-card` + `detail-card` 模式

### CommitteeMemberManage.vue
- 操作区块用 `section-card`

---

## 六、迁移步骤建议

1. **第一批(高频页面)**:Dashboard、MyWork、UserManage、CommunityOverview、GovernanceOverview
2. **第二批(内容相关)**:ContentList、ContentCalendar、ContentEdit、PublishView
3. **第三批(治理相关)**:CommitteeList、CommitteeDetail、MeetingCalendar、MeetingDetail
4. **第四批(其余)**:CommitteeMemberManage、Settings、CommunityManage

每批完成后验证页面功能和视觉效果后再进行下一批。
Loading
Loading