|
|
@@ -0,0 +1,499 @@
|
|
|
+# 待办事项标签筛选功能 - 代码总结
|
|
|
+
|
|
|
+## 📄 概览
|
|
|
+
|
|
|
+本文档提供标签筛选功能实现的代码要点总结,便于快速查阅和维护。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1️⃣ TypeScript 实现 (dashboard.ts)
|
|
|
+
|
|
|
+### 1.1 状态定义
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 位置:Line 169-170
|
|
|
+
|
|
|
+// 标签过滤状态(当前选中的筛选标签)
|
|
|
+urgentEventTagFilter = signal<'all' | 'customer' | 'phase' | 'review' | 'delivery'>('all');
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 使用 Angular Signal 存储当前的筛选标签
|
|
|
+- 初始值为 `'all'`(显示全部)
|
|
|
+- 可能的值:`all` | `customer` | `phase` | `review` | `delivery`
|
|
|
+
|
|
|
+### 1.2 标签统计计算属性
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 位置:Line 172-202
|
|
|
+
|
|
|
+urgentEventTags = computed(() => {
|
|
|
+ const events = this.urgentEventsList();
|
|
|
+
|
|
|
+ // 按类型分类统计
|
|
|
+ let customerCount = 0;
|
|
|
+ let phaseCount = 0;
|
|
|
+ let reviewCount = 0;
|
|
|
+ let deliveryCount = 0;
|
|
|
+
|
|
|
+ events.forEach(event => {
|
|
|
+ if (event.eventType === 'review') {
|
|
|
+ reviewCount++;
|
|
|
+ } else if (event.eventType === 'delivery') {
|
|
|
+ deliveryCount++;
|
|
|
+ } else if (event.eventType === 'phase_deadline') {
|
|
|
+ phaseCount++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 构建有序标签列表
|
|
|
+ const tags = [
|
|
|
+ { id: 'customer', label: '客户服务', count: customerCount, icon: '👥' },
|
|
|
+ { id: 'phase', label: '工作阶段', count: phaseCount, icon: '🔧' },
|
|
|
+ { id: 'review', label: '小图截止', count: reviewCount, icon: '📐' },
|
|
|
+ { id: 'delivery', label: '交付延期', count: deliveryCount, icon: '📦' }
|
|
|
+ ];
|
|
|
+
|
|
|
+ return tags.filter(tag => tag.count > 0);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 遍历所有事件,按类型统计数量
|
|
|
+- 构建标签对象数组,包含 id、label、count、icon
|
|
|
+- 只返回 count > 0 的标签(隐藏无事件的标签)
|
|
|
+- 标签顺序遵循优先级:客户 > 工作阶段 > 小图截止 > 交付延期
|
|
|
+
|
|
|
+### 1.3 过滤结果计算属性
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 位置:Line 204-227
|
|
|
+
|
|
|
+filteredUrgentEvents = computed(() => {
|
|
|
+ const events = this.urgentEventsList();
|
|
|
+ const filter = this.urgentEventTagFilter();
|
|
|
+
|
|
|
+ if (filter === 'all') {
|
|
|
+ return events;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按标签过滤
|
|
|
+ switch (filter) {
|
|
|
+ case 'customer':
|
|
|
+ return events.filter(e => e.eventType === 'review');
|
|
|
+ case 'phase':
|
|
|
+ return events.filter(e => e.eventType === 'phase_deadline');
|
|
|
+ case 'review':
|
|
|
+ return events.filter(e => e.eventType === 'review');
|
|
|
+ case 'delivery':
|
|
|
+ return events.filter(e => e.eventType === 'delivery');
|
|
|
+ default:
|
|
|
+ return events;
|
|
|
+ }
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 根据当前的 `urgentEventTagFilter` 值过滤事件
|
|
|
+- `all` 时返回所有事件
|
|
|
+- 其他情况通过 Switch 语句匹配事件类型
|
|
|
+- 返回筛选后的事件数组
|
|
|
+
|
|
|
+**注意**:`customer` 标签映射到 `review` 类型事件(可根据需求调整)
|
|
|
+
|
|
|
+### 1.4 标签点击处理方法
|
|
|
+
|
|
|
+```typescript
|
|
|
+// 位置:Line 1981-1987
|
|
|
+
|
|
|
+/**
|
|
|
+ * 待办事项标签筛选
|
|
|
+ */
|
|
|
+filterUrgentEventsByTag(tag: 'all' | 'customer' | 'phase' | 'review' | 'delivery'): void {
|
|
|
+ console.log(`🏷️ [紧急事件] 应用标签筛选: ${tag}`);
|
|
|
+ this.urgentEventTagFilter.set(tag);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 处理用户点击标签时的事件
|
|
|
+- 更新 `urgentEventTagFilter` Signal,自动触发 `filteredUrgentEvents` 重新计算
|
|
|
+- 添加日志便于调试
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2️⃣ HTML 实现 (dashboard.html)
|
|
|
+
|
|
|
+### 2.1 标签筛选栏 UI
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 位置:Line 275-302 -->
|
|
|
+
|
|
|
+<!-- 标签筛选栏 -->
|
|
|
+@if (urgentEventsList().length > 0) {
|
|
|
+ <div class="tag-filter-bar">
|
|
|
+ <!-- "全部"标签 -->
|
|
|
+ <button
|
|
|
+ class="tag-button"
|
|
|
+ [class.active]="urgentEventTagFilter() === 'all'"
|
|
|
+ (click)="filterUrgentEventsByTag('all')"
|
|
|
+ title="显示所有待办事项"
|
|
|
+ >
|
|
|
+ <span class="tag-icon">📋</span>
|
|
|
+ <span class="tag-label">全部</span>
|
|
|
+ <span class="tag-count">{{ urgentEventsList().length }}</span>
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <!-- 动态标签 -->
|
|
|
+ @for (tag of urgentEventTags(); track tag.id) {
|
|
|
+ <button
|
|
|
+ class="tag-button"
|
|
|
+ [class.active]="urgentEventTagFilter() === tag.id"
|
|
|
+ (click)="filterUrgentEventsByTag(tag.id as any)"
|
|
|
+ [title]="tag.label + ' (' + tag.count + '个)'"
|
|
|
+ >
|
|
|
+ <span class="tag-icon">{{ tag.icon }}</span>
|
|
|
+ <span class="tag-label">{{ tag.label }}</span>
|
|
|
+ <span class="tag-count">{{ tag.count }}</span>
|
|
|
+ </button>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 外层 `@if` 控制标签栏仅在有事件时显示
|
|
|
+- "全部"按钮固定显示
|
|
|
+- 动态标签通过 `@for` 循环生成
|
|
|
+- `[class.active]` 绑定当前活跃状态
|
|
|
+- `(click)` 绑定点击事件处理
|
|
|
+- `title` 属性提供无障碍支持
|
|
|
+
|
|
|
+### 2.2 修改事件列表循环
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 位置:Line 326-328 -->
|
|
|
+
|
|
|
+<!-- 原:@for (event of urgentEventsList(); track event.id) -->
|
|
|
+<!-- 改为:-->
|
|
|
+@if (!loadingUrgentEvents() && filteredUrgentEvents().length > 0) {
|
|
|
+ <div class="todo-list-compact urgent-list">
|
|
|
+ @for (event of filteredUrgentEvents(); track event.id) {
|
|
|
+ <!-- 事件项内容保持不变 -->
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 将循环数据源从 `urgentEventsList()` 改为 `filteredUrgentEvents()`
|
|
|
+- 自动根据筛选标签动态更新显示列表
|
|
|
+
|
|
|
+### 2.3 添加空结果提示
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 位置:Line 421-430 -->
|
|
|
+
|
|
|
+<!-- 过滤后没有结果的空状态 -->
|
|
|
+@if (!loadingUrgentEvents() && urgentEventsList().length > 0 && filteredUrgentEvents().length === 0) {
|
|
|
+ <div class="empty-state filtered">
|
|
|
+ <svg viewBox="0 0 24 24" width="48" height="48" fill="#d1d5db">
|
|
|
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>
|
|
|
+ </svg>
|
|
|
+ <p>该筛选条件下暂无事件</p>
|
|
|
+ <p class="hint">尝试调整筛选条件</p>
|
|
|
+ </div>
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 仅在以下条件全部满足时显示:
|
|
|
+ - 未加载中(`!loadingUrgentEvents()`)
|
|
|
+ - 原始事件不为空(`urgentEventsList().length > 0`)
|
|
|
+ - 过滤结果为空(`filteredUrgentEvents().length === 0`)
|
|
|
+- 提供友好的空状态提示
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3️⃣ SCSS 样式 (dashboard-urgent-tasks-enhanced.scss)
|
|
|
+
|
|
|
+### 3.1 标签栏容器
|
|
|
+
|
|
|
+```scss
|
|
|
+// 位置:Line 3-32
|
|
|
+
|
|
|
+.tag-filter-bar {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ padding: 12px 16px;
|
|
|
+ background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
|
|
|
+ border-bottom: 1px solid #e0e6ed;
|
|
|
+ overflow-x: auto;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ // 自定义滚动条样式...
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**关键属性**:
|
|
|
+- `display: flex`:水平排列标签
|
|
|
+- `overflow-x: auto`:允许水平滚动
|
|
|
+- `gradient`:美观的渐变背景
|
|
|
+- `gap: 8px`:标签间距
|
|
|
+
|
|
|
+### 3.2 标签按钮样式
|
|
|
+
|
|
|
+```scss
|
|
|
+// 位置:Line 34-110
|
|
|
+
|
|
|
+.tag-button {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ padding: 6px 12px;
|
|
|
+ background: #ffffff;
|
|
|
+ border: 1.5px solid #dfe3e6;
|
|
|
+ border-radius: 20px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #4a5568;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ white-space: nowrap;
|
|
|
+ flex-shrink: 0;
|
|
|
+
|
|
|
+ // 悬停状态
|
|
|
+ &:hover {
|
|
|
+ border-color: #cbd5e0;
|
|
|
+ background: #f8fafc;
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 激活状态
|
|
|
+ &.active {
|
|
|
+ background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%);
|
|
|
+ border-color: #4f46e5;
|
|
|
+ color: #ffffff;
|
|
|
+ box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**关键属性**:
|
|
|
+- `border-radius: 20px`:圆形边框
|
|
|
+- `transition: all 0.3s ease`:平滑过渡
|
|
|
+- `transform: translateY(-2px)`:悬停提升效果
|
|
|
+- 激活状态使用渐变背景和阴影
|
|
|
+
|
|
|
+### 3.3 响应式设计
|
|
|
+
|
|
|
+```scss
|
|
|
+// 位置:Line 149-180
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .tag-filter-bar {
|
|
|
+ padding: 10px 12px;
|
|
|
+ gap: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tag-button {
|
|
|
+ padding: 5px 10px;
|
|
|
+ font-size: 11px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 480px) {
|
|
|
+ .tag-filter-bar {
|
|
|
+ padding: 8px 10px;
|
|
|
+ gap: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .tag-button {
|
|
|
+ padding: 4px 8px;
|
|
|
+ font-size: 10px;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**说明**:
|
|
|
+- 平板(≤768px):压缩间距和字体
|
|
|
+- 手机(≤480px):进一步压缩尺寸
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4️⃣ 数据类型定义
|
|
|
+
|
|
|
+### 标签对象接口
|
|
|
+
|
|
|
+```typescript
|
|
|
+interface TagItem {
|
|
|
+ id: 'customer' | 'phase' | 'review' | 'delivery';
|
|
|
+ label: string;
|
|
|
+ count: number;
|
|
|
+ icon: string;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 紧急事件类型
|
|
|
+
|
|
|
+```typescript
|
|
|
+interface UrgentEvent {
|
|
|
+ id: string;
|
|
|
+ title: string;
|
|
|
+ description: string;
|
|
|
+ eventType: 'review' | 'delivery' | 'phase_deadline';
|
|
|
+ phaseName?: string;
|
|
|
+ deadline: Date;
|
|
|
+ projectId: string;
|
|
|
+ projectName: string;
|
|
|
+ designerName?: string;
|
|
|
+ urgencyLevel: 'critical' | 'high' | 'medium';
|
|
|
+ overdueDays?: number;
|
|
|
+ completionRate?: number;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5️⃣ 事件类型到标签的映射
|
|
|
+
|
|
|
+| eventType | 标签 ID | 标签名 | 图标 |
|
|
|
+|-----------|--------|-------|------|
|
|
|
+| `'review'` | `'review'` | 小图截止 | 📐 |
|
|
|
+| `'delivery'` | `'delivery'` | 交付延期 | 📦 |
|
|
|
+| `'phase_deadline'` | `'phase'` | 工作阶段 | 🔧 |
|
|
|
+| - | `'customer'` | 客户服务 | 👥 |
|
|
|
+
|
|
|
+**注**:`'customer'` 标签目前映射到 `'review'` 事件类型,可根据需求调整。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6️⃣ 关键流程图
|
|
|
+
|
|
|
+```
|
|
|
+用户界面
|
|
|
+ ↓
|
|
|
+click tag button
|
|
|
+ ↓
|
|
|
+filterUrgentEventsByTag(tag)
|
|
|
+ ↓
|
|
|
+urgentEventTagFilter.set(tag)
|
|
|
+ ↓
|
|
|
+filteredUrgentEvents computed 重新计算
|
|
|
+ ↓
|
|
|
+@for (event of filteredUrgentEvents())
|
|
|
+ ↓
|
|
|
+页面显示更新
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7️⃣ 修改检查清单
|
|
|
+
|
|
|
+在维护或修改此功能时,确保检查以下要点:
|
|
|
+
|
|
|
+### TypeScript (dashboard.ts)
|
|
|
+- [ ] `urgentEventTagFilter` Signal 初始值正确
|
|
|
+- [ ] `urgentEventTags` 计算逻辑准确
|
|
|
+- [ ] `filteredUrgentEvents` 过滤条件完整
|
|
|
+- [ ] `filterUrgentEventsByTag()` 方法签名一致
|
|
|
+- [ ] 无 TypeScript 编译错误
|
|
|
+
|
|
|
+### HTML (dashboard.html)
|
|
|
+- [ ] 标签栏仅在 `urgentEventsList().length > 0` 时显示
|
|
|
+- [ ] 标签点击事件绑定正确
|
|
|
+- [ ] 激活状态绑定正确 `[class.active]="..."`
|
|
|
+- [ ] 事件列表循环使用 `filteredUrgentEvents()`
|
|
|
+- [ ] 空结果提示条件正确
|
|
|
+
|
|
|
+### SCSS (dashboard-urgent-tasks-enhanced.scss)
|
|
|
+- [ ] 导入到 `dashboard.ts` 的 `styleUrls`
|
|
|
+- [ ] 响应式媒体查询完整
|
|
|
+- [ ] 颜色和间距满足设计规范
|
|
|
+- [ ] 不与其他样式冲突
|
|
|
+
|
|
|
+### 测试
|
|
|
+- [ ] 标签显示和计数正确
|
|
|
+- [ ] 点击标签过滤生效
|
|
|
+- [ ] 无结果时显示提示
|
|
|
+- [ ] 响应式适配正常
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8️⃣ 常见修改场景
|
|
|
+
|
|
|
+### 场景 1:添加新的事件类型标签
|
|
|
+
|
|
|
+**步骤**:
|
|
|
+1. 在 TypeScript 中 `urgentEventTagFilter` 类型中添加新值
|
|
|
+2. 在 `urgentEventTags` 中添加新的计数逻辑
|
|
|
+3. 在 `filteredUrgentEvents` 中添加新的过滤 case
|
|
|
+4. 在 HTML 中自动显示(通过 `@for` 循环)
|
|
|
+5. 在 SCSS 中可选添加特色样式
|
|
|
+
|
|
|
+### 场景 2:修改标签颜色
|
|
|
+
|
|
|
+**步骤**:
|
|
|
+1. 在 `dashboard-urgent-tasks-enhanced.scss` 中修改 `.tag-button.active` 的背景色
|
|
|
+2. 或添加 `data-tag-type` 属性用于不同标签的颜色
|
|
|
+
|
|
|
+### 场景 3:修改过滤逻辑
|
|
|
+
|
|
|
+**步骤**:
|
|
|
+1. 编辑 `filteredUrgentEvents` 中的 switch case
|
|
|
+2. 确保对应的计数逻辑也一致更新
|
|
|
+3. 测试筛选功能
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9️⃣ 性能优化建议
|
|
|
+
|
|
|
+1. **Signals 自动优化**:已使用 Angular Signals,自动处理变化检测
|
|
|
+2. **计算属性缓存**:`computed()` 自动缓存,只在依赖变化时重新计算
|
|
|
+3. **track 函数**:在 `@for` 中使用 `track event.id`,避免不必要的 DOM 重排
|
|
|
+4. **OnDestroy**:原有的销毁逻辑已完整,无额外清理需求
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 🔟 调试技巧
|
|
|
+
|
|
|
+### 在浏览器控制台检查状态
|
|
|
+
|
|
|
+```javascript
|
|
|
+// 进入 Angular 应用的 ng zone
|
|
|
+ng.getComponent(document.body).urgentEventTagFilter()
|
|
|
+
|
|
|
+// 查看过滤后的事件
|
|
|
+ng.getComponent(document.body).filteredUrgentEvents()
|
|
|
+
|
|
|
+// 查看标签统计
|
|
|
+ng.getComponent(document.body).urgentEventTags()
|
|
|
+```
|
|
|
+
|
|
|
+### 添加临时日志
|
|
|
+
|
|
|
+在 `filterUrgentEventsByTag` 方法中已有日志:
|
|
|
+```typescript
|
|
|
+console.log(`🏷️ [紧急事件] 应用标签筛选: ${tag}`);
|
|
|
+```
|
|
|
+
|
|
|
+在 `filteredUrgentEvents` 中可添加:
|
|
|
+```typescript
|
|
|
+console.log('筛选结果:', filteredUrgentEvents);
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 参考文档
|
|
|
+
|
|
|
+- 实现说明:[待办事项标签筛选功能实现说明.md](./待办事项标签筛选功能实现说明.md)
|
|
|
+- 测试指南:[待办事项标签筛选功能测试指南.md](./待办事项标签筛选功能测试指南.md)
|
|
|
+- 快速开始:[URGENT-TODO-TAG-FILTER-QUICKSTART.md](./URGENT-TODO-TAG-FILTER-QUICKSTART.md)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+**版本**:1.0.0
|
|
|
+**最后更新**:2025-11-18
|
|
|
+**维护人员**:设计组
|
|
|
+
|