Browse Source

docs: add revision task dashboard integration documentation

- Created comprehensive guide for integrating revision task approval into designer dashboard
- Documented TypeScript implementation including service injection, data loading, and approval workflow methods
- Provided complete HTML template structure for task cards and approval modal with detailed markup examples
- Included extensive SCSS styling specifications for task cards, badges, space tags, and modal components
徐福静0235668 7 hours ago
parent
commit
49db393e19

+ 826 - 0
docs/revision-task-dashboard-integration.md

@@ -0,0 +1,826 @@
+# 改图任务Dashboard集成文档
+
+## 📋 功能概述
+
+在设计师组长Dashboard的紧急事件板块增加改图任务审批功能,显示待审批的大修改工单,并提供审批操作。
+
+---
+
+## 🎯 实现位置
+
+**Dashboard位置**:`src/app/pages/designer/dashboard/`
+
+**显示区域**:附加信息区域(additional-info-section),位于"待处理反馈"和"代班信息"之间
+
+---
+
+## ✨ 核心功能
+
+### 1. 改图任务卡片显示
+- **显示位置**:紧急事件板块的橙色区域
+- **显示条件**:`pendingRevisionTasks.length > 0`
+- **卡片内容**:
+  - 工单编号(前8位)
+  - 大修改标签
+  - 项目名称(可点击跳转)
+  - 涉及空间标签
+  - 修改内容描述
+  - 创建人信息
+  - 创建时间(相对时间)
+  - 审批按钮
+
+### 2. 审批弹窗
+- **触发方式**:点击卡片上的"审批"按钮
+- **弹窗内容**:
+  - 工单完整信息
+  - 审批选项(通过/驳回)
+  - 通过:可选备注
+  - 驳回:必填原因
+  - 确认/取消按钮
+
+---
+
+## 📂 修改文件
+
+### 1. TypeScript (`dashboard.ts`)
+
+#### 新增导入
+```typescript
+import { RevisionTaskService, RevisionTask } from '../../../services/revision-task.service';
+```
+
+#### 新增属性
+```typescript
+// 改图工单相关
+pendingRevisionTasks: RevisionTask[] = []; // 待审批的改图工单
+showRevisionApprovalModal: boolean = false; // 审批弹窗
+currentRevisionTask: RevisionTask | null = null; // 当前审批的工单
+revisionApprovalForm = {
+  action: 'approve' as 'approve' | 'reject',
+  notes: '',
+  rejectionReason: ''
+};
+```
+
+#### 构造函数注入
+```typescript
+constructor(
+  // ... 其他服务
+  private revisionTaskService: RevisionTaskService
+) {}
+```
+
+#### 数据加载
+```typescript
+await Promise.all([
+  // ... 其他加载
+  this.loadPendingRevisionTasks() // 🆕 加载待审批改图工单
+]);
+```
+
+#### 核心方法
+
+**1. 加载待审批工单**
+```typescript
+private async loadPendingRevisionTasks(): Promise<void> {
+  // 查询所有项目的revisionTasks
+  // 筛选status === 'pending_approval'
+  // 按创建时间倒序排列
+}
+```
+
+**2. 打开审批弹窗**
+```typescript
+openRevisionApprovalModal(task: RevisionTask): void {
+  this.currentRevisionTask = task;
+  this.revisionApprovalForm = {
+    action: 'approve',
+    notes: '',
+    rejectionReason: ''
+  };
+  this.showRevisionApprovalModal = true;
+}
+```
+
+**3. 提交审批**
+```typescript
+async submitRevisionApproval(): Promise<void> {
+  if (action === 'approve') {
+    await this.revisionTaskService.approveRevisionTask(...);
+  } else {
+    // 验证驳回原因
+    await this.revisionTaskService.rejectRevisionTask(...);
+  }
+  // 重新加载待审批工单
+  await this.loadPendingRevisionTasks();
+}
+```
+
+**4. 跳转到项目**
+```typescript
+goToRevisionProject(task: RevisionTask): void {
+  this.router.navigate(['/wxwork', this.cid, 'project', task.projectId]);
+}
+```
+
+**5. 格式化时间**
+```typescript
+formatRevisionTime(date: Date): string {
+  // 刚刚、X分钟前、X小时前、X天前
+  // 超过7天显示日期
+}
+```
+
+---
+
+### 2. HTML模板 (`dashboard.html`)
+
+#### 改图任务卡片区域(Line 171-228)
+```html
+<!-- 改图任务审批区域 -->
+<div class="info-column revision-tasks-column" *ngIf="pendingRevisionTasks.length > 0">
+  <div class="section-header">
+    <h2>
+      <svg>...</svg>
+      改图任务 ({{ pendingRevisionTasks.length }})
+    </h2>
+  </div>
+  
+  <div class="revision-task-list">
+    <div *ngFor="let task of pendingRevisionTasks" class="revision-task-card">
+      <!-- 工单头部 -->
+      <div class="task-header">
+        <span class="task-id">#{{ task.id?.substring(0, 8) }}</span>
+        <span class="task-badge major">大修改</span>
+      </div>
+      
+      <!-- 项目名称(可点击) -->
+      <div class="task-project" (click)="goToRevisionProject(task)">
+        <svg>...</svg>
+        {{ task.projectName }}
+      </div>
+      
+      <!-- 涉及空间 -->
+      <div class="task-spaces" *ngIf="task.spaceNames && task.spaceNames.length > 0">
+        <label>涉及空间:</label>
+        <div class="space-tags">
+          <span *ngFor="let space of task.spaceNames" class="space-tag">{{ space }}</span>
+        </div>
+      </div>
+      
+      <!-- 修改内容 -->
+      <div class="task-description">
+        <label>修改内容:</label>
+        <p>{{ task.description }}</p>
+      </div>
+      
+      <!-- 元信息 -->
+      <div class="task-meta">
+        <span class="task-creator">
+          <svg>...</svg>
+          {{ task.createdByName }}
+        </span>
+        <span class="task-time">{{ formatRevisionTime(task.createdAt) }}</span>
+      </div>
+      
+      <!-- 审批按钮 -->
+      <div class="task-actions">
+        <button class="btn-approve" (click)="openRevisionApprovalModal(task)">
+          <svg>...</svg>
+          审批
+        </button>
+      </div>
+    </div>
+  </div>
+</div>
+```
+
+#### 审批弹窗(Line 459-580)
+```html
+<!-- 改图工单审批弹窗 -->
+@if (showRevisionApprovalModal && currentRevisionTask) {
+  <div class="modal-overlay" (click)="closeRevisionApprovalModal()">
+    <div class="revision-approval-modal" (click)="$event.stopPropagation()">
+      <div class="modal-header">
+        <h3>审批改图工单</h3>
+        <button class="close-btn" (click)="closeRevisionApprovalModal()">×</button>
+      </div>
+      
+      <div class="modal-body">
+        <!-- 工单信息 -->
+        <div class="task-info-section">
+          <div class="info-row">
+            <label>工单编号:</label>
+            <span>#{{ currentRevisionTask.id?.substring(0, 8) }}</span>
+          </div>
+          <!-- ... 更多信息 -->
+        </div>
+        
+        <!-- 审批操作 -->
+        <div class="approval-action-section">
+          <div class="action-type-selector">
+            <label class="radio-label" [class.active]="revisionApprovalForm.action === 'approve'">
+              <input type="radio" name="action" value="approve" [(ngModel)]="revisionApprovalForm.action">
+              <svg>...</svg>
+              <span>通过审批</span>
+            </label>
+            
+            <label class="radio-label" [class.active]="revisionApprovalForm.action === 'reject'">
+              <input type="radio" name="action" value="reject" [(ngModel)]="revisionApprovalForm.action">
+              <svg>...</svg>
+              <span>驳回</span>
+            </label>
+          </div>
+          
+          <!-- 备注/驳回原因 -->
+          <div class="form-group" *ngIf="revisionApprovalForm.action === 'approve'">
+            <label>审批备注(可选)</label>
+            <textarea [(ngModel)]="revisionApprovalForm.notes"></textarea>
+          </div>
+          
+          <div class="form-group" *ngIf="revisionApprovalForm.action === 'reject'">
+            <label>驳回原因 <span class="required">*</span></label>
+            <textarea [(ngModel)]="revisionApprovalForm.rejectionReason"></textarea>
+          </div>
+        </div>
+      </div>
+      
+      <div class="modal-footer">
+        <button class="btn-cancel" (click)="closeRevisionApprovalModal()">取消</button>
+        <button 
+          class="btn-submit" 
+          [class.btn-approve]="revisionApprovalForm.action === 'approve'"
+          [class.btn-reject]="revisionApprovalForm.action === 'reject'"
+          (click)="submitRevisionApproval()">
+          {{ revisionApprovalForm.action === 'approve' ? '确认通过' : '确认驳回' }}
+        </button>
+      </div>
+    </div>
+  </div>
+}
+```
+
+---
+
+### 3. SCSS样式 (`dashboard.scss`)
+
+需要添加的样式类:
+
+#### 改图任务卡片样式
+```scss
+// 改图任务列表容器
+.revision-tasks-column {
+  background: linear-gradient(135deg, #fff5f0 0%, #ffffff 100%);
+  border-left: 4px solid #FF6B35;
+}
+
+// 改图任务卡片
+.revision-task-card {
+  background: #ffffff;
+  border-radius: 12px;
+  padding: 16px;
+  margin-bottom: 16px;
+  border: 1px solid #ffe4d6;
+  box-shadow: 0 2px 8px rgba(255, 107, 53, 0.1);
+  transition: all 0.3s ease;
+  
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 16px rgba(255, 107, 53, 0.15);
+  }
+}
+
+// 工单头部
+.task-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.task-id {
+  font-family: 'Courier New', monospace;
+  color: #666;
+  font-size: 12px;
+}
+
+.task-badge.major {
+  background: linear-gradient(135deg, #FF6B35 0%, #FF8F5C 100%);
+  color: white;
+  padding: 4px 12px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: 600;
+}
+
+// 项目名称(可点击)
+.task-project {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  color: #0047AB;
+  font-weight: 500;
+  margin-bottom: 12px;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  
+  &:hover {
+    color: #0052C9;
+    text-decoration: underline;
+  }
+  
+  svg {
+    flex-shrink: 0;
+  }
+}
+
+// 空间标签
+.task-spaces {
+  margin-bottom: 12px;
+  
+  label {
+    font-size: 12px;
+    color: #666;
+    margin-bottom: 6px;
+    display: block;
+  }
+}
+
+.space-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+}
+
+.space-tag {
+  background: #e8f4ff;
+  color: #0047AB;
+  padding: 4px 10px;
+  border-radius: 8px;
+  font-size: 12px;
+  font-weight: 500;
+}
+
+// 修改描述
+.task-description {
+  margin-bottom: 12px;
+  
+  label {
+    font-size: 12px;
+    color: #666;
+    margin-bottom: 4px;
+    display: block;
+  }
+  
+  p {
+    font-size: 14px;
+    color: #333;
+    margin: 0;
+    line-height: 1.6;
+  }
+}
+
+// 元信息
+.task-meta {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  font-size: 12px;
+  color: #999;
+  margin-bottom: 12px;
+}
+
+.task-creator {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+// 审批按钮
+.task-actions {
+  display: flex;
+  justify-content: flex-end;
+}
+
+.btn-approve {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  background: linear-gradient(135deg, #34C759 0%, #4CD964 100%);
+  color: white;
+  border: none;
+  padding: 8px 16px;
+  border-radius: 8px;
+  font-size: 13px;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  box-shadow: 0 2px 8px rgba(52, 199, 89, 0.25);
+  
+  &:hover {
+    background: linear-gradient(135deg, #30B350 0%, #43C65A 100%);
+    transform: translateY(-1px);
+    box-shadow: 0 4px 12px rgba(52, 199, 89, 0.35);
+  }
+  
+  &:active {
+    transform: translateY(0);
+  }
+}
+```
+
+#### 审批弹窗样式
+```scss
+.revision-approval-modal {
+  background: white;
+  border-radius: 16px;
+  width: 600px;
+  max-width: 90vw;
+  max-height: 85vh;
+  overflow-y: auto;
+  
+  .modal-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 20px 24px;
+    border-bottom: 1px solid #e5e5ea;
+    
+    h3 {
+      display: flex;
+      align-items: center;
+      font-size: 18px;
+      font-weight: 600;
+      margin: 0;
+    }
+    
+    .close-btn {
+      background: none;
+      border: none;
+      font-size: 28px;
+      cursor: pointer;
+      color: #999;
+      
+      &:hover {
+        color: #333;
+      }
+    }
+  }
+  
+  .modal-body {
+    padding: 24px;
+  }
+  
+  // 工单信息区
+  .task-info-section {
+    background: #f8f9fa;
+    border-radius: 12px;
+    padding: 16px;
+    margin-bottom: 24px;
+    
+    .info-row {
+      display: flex;
+      margin-bottom: 12px;
+      
+      &:last-child {
+        margin-bottom: 0;
+      }
+      
+      label {
+        font-weight: 600;
+        color: #666;
+        min-width: 90px;
+        flex-shrink: 0;
+      }
+      
+      span {
+        color: #333;
+      }
+    }
+    
+    .description-row {
+      flex-direction: column;
+      
+      .description-text {
+        margin-top: 8px;
+        line-height: 1.6;
+        color: #333;
+      }
+    }
+  }
+  
+  .space-tags-inline {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 6px;
+  }
+  
+  .space-tag-small {
+    background: #e8f4ff;
+    color: #0047AB;
+    padding: 2px 8px;
+    border-radius: 6px;
+    font-size: 12px;
+  }
+  
+  // 审批操作区
+  .approval-action-section {
+    .action-type-selector {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 12px;
+      margin-bottom: 20px;
+    }
+    
+    .radio-label {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+      padding: 12px 16px;
+      border: 2px solid #e5e5ea;
+      border-radius: 12px;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      
+      input[type="radio"] {
+        display: none;
+      }
+      
+      svg {
+        flex-shrink: 0;
+      }
+      
+      &.active {
+        border-color: #34C759;
+        background: rgba(52, 199, 89, 0.05);
+        
+        &:nth-child(2) {
+          border-color: #FF3B30;
+          background: rgba(255, 59, 48, 0.05);
+        }
+      }
+      
+      &:hover {
+        border-color: #cbd5e1;
+      }
+    }
+    
+    .form-group {
+      label {
+        display: block;
+        font-weight: 600;
+        margin-bottom: 8px;
+        color: #333;
+        
+        .required {
+          color: #FF3B30;
+        }
+      }
+      
+      textarea {
+        width: 100%;
+        padding: 12px;
+        border: 1px solid #e5e5ea;
+        border-radius: 8px;
+        font-size: 14px;
+        font-family: inherit;
+        resize: vertical;
+        
+        &:focus {
+          outline: none;
+          border-color: #0047AB;
+        }
+      }
+    }
+  }
+  
+  .modal-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 12px;
+    padding: 20px 24px;
+    border-top: 1px solid #e5e5ea;
+    
+    button {
+      padding: 10px 24px;
+      border-radius: 8px;
+      font-size: 14px;
+      font-weight: 600;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      border: none;
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+    }
+    
+    .btn-cancel {
+      background: #f1f3f5;
+      color: #666;
+      
+      &:hover {
+        background: #e9ecef;
+      }
+    }
+    
+    .btn-submit {
+      color: white;
+      
+      &.btn-approve {
+        background: linear-gradient(135deg, #34C759 0%, #4CD964 100%);
+        box-shadow: 0 2px 8px rgba(52, 199, 89, 0.25);
+        
+        &:hover {
+          background: linear-gradient(135deg, #30B350 0%, #43C65A 100%);
+          box-shadow: 0 4px 12px rgba(52, 199, 89, 0.35);
+        }
+      }
+      
+      &.btn-reject {
+        background: linear-gradient(135deg, #FF3B30 0%, #FF5A4F 100%);
+        box-shadow: 0 2px 8px rgba(255, 59, 48, 0.25);
+        
+        &:hover {
+          background: linear-gradient(135deg, #E6352B 0%, #FF5046 100%);
+          box-shadow: 0 4px 12px rgba(255, 59, 48, 0.35);
+        }
+      }
+    }
+  }
+}
+```
+
+---
+
+## 🔄 数据流
+
+### 加载流程
+```
+1. Dashboard初始化
+   ↓
+2. authenticateAndLoadData()
+   ↓
+3. loadDashboardData()
+   ↓
+4. loadPendingRevisionTasks()
+   ↓
+5. 查询Project.data.revisionTasks
+   ↓
+6. 筛选status === 'pending_approval'
+   ↓
+7. 填充projectName和projectId(从Project对象)
+   ↓
+8. 按创建时间倒序排列
+   ↓
+9. 显示在Dashboard
+```
+
+### 审批流程
+```
+1. 点击"审批"按钮
+   ↓
+2. openRevisionApprovalModal(task)
+   ↓
+3. 显示审批弹窗
+   ↓
+4. 选择通过/驳回
+   ↓
+5. submitRevisionApproval()
+   ↓
+6. 调用RevisionTaskService
+   ↓
+7. 更新Project.data.revisionTasks
+   ↓
+8. 重新加载待审批工单
+   ↓
+9. 显示成功提示
+```
+
+---
+
+## 📊 UI设计
+
+### 卡片布局
+```
+┌────────────────────────────────────────┐
+│ 改图任务 (2)                          │
+├────────────────────────────────────────┤
+│ ┌────────────────────────────────────┐ │
+│ │ #RT_17... [大修改]                 │ │
+│ │                                    │ │
+│ │ 🏠 现代简约客厅设计项目             │ │
+│ │                                    │ │
+│ │ 涉及空间:[主卧] [次卧] [客厅]     │ │
+│ │                                    │ │
+│ │ 修改内容:主卧的整体氛围需要调整    │ │
+│ │                                    │ │
+│ │ 👤 刘雨熙   18分钟前               │ │
+│ │                                    │ │
+│ │                         [✓ 审批]   │ │
+│ └────────────────────────────────────┘ │
+└────────────────────────────────────────┘
+```
+
+### 审批弹窗
+```
+┌─────────────────────────────────────────┐
+│ 📅 审批改图工单                    ×   │
+├─────────────────────────────────────────┤
+│ 工单编号:#RT_17652                     │
+│ 项目名称:现代简约客厅设计项目           │
+│ 创建人:刘雨熙 (designer)              │
+│ 创建时间:18分钟前                      │
+│ 涉及空间:[主卧] [次卧] [客厅]         │
+│ 预计时间:2-3天                         │
+│ 修改内容:主卧的整体氛围需要调整...      │
+│                                         │
+│ ┌───────────┐  ┌──────────┐           │
+│ │ ✓ 通过审批 │  │ × 驳回   │           │
+│ └───────────┘  └──────────┘           │
+│                                         │
+│ 审批备注(可选):                      │
+│ ┌─────────────────────────────────┐   │
+│ │ 同意修改,请尽快完成...         │   │
+│ └─────────────────────────────────┘   │
+│                                         │
+│                  [取消] [✓ 确认通过]   │
+└─────────────────────────────────────────┘
+```
+
+---
+
+## 🎨 样式设计
+
+### 配色方案
+- **主色**:橙色系 (#FF6B35) - 警示色,突出紧急性
+- **成功色**:绿色 (#34C759) - 通过审批
+- **危险色**:红色 (#FF3B30) - 驳回
+- **背景色**:白色 + 浅橙色渐变
+
+### iOS风格特点
+1. **圆角**:8-16px
+2. **渐变**:135度线性渐变
+3. **阴影**:轻柔立体
+4. **动画**:0.2-0.3s平滑过渡
+5. **交互反馈**:悬停浮起、点击缩放
+
+---
+
+## 🧪 测试要点
+
+### 功能测试
+- [ ] 待审批工单正确加载
+- [ ] 工单数量显示正确
+- [ ] 点击项目名称跳转正确
+- [ ] 审批弹窗正常打开/关闭
+- [ ] 通过审批成功
+- [ ] 驳回审批成功(含必填验证)
+- [ ] 审批后列表自动刷新
+
+### UI测试
+- [ ] 卡片布局正常
+- [ ] 标签显示正确
+- [ ] 时间格式化正确
+- [ ] 弹窗居中显示
+- [ ] 按钮样式正确
+- [ ] 响应式布局正常
+
+### 边界测试
+- [ ] 无待审批工单时不显示
+- [ ] 驳回时未填原因提示
+- [ ] 网络错误处理
+- [ ] 空数据处理
+
+---
+
+## 📝 注意事项
+
+1. **权限控制**:当前实现未做权限验证,所有登录用户都可以审批
+2. **通知功能**:审批后的通知功能需要实现
+3. **批量操作**:未实现批量审批功能
+4. **历史记录**:未显示已审批的工单历史
+5. **性能优化**:大量工单时需要分页
+
+---
+
+## 🚀 后续优化
+
+### 短期优化
+- [ ] 添加角色权限验证(仅组长可审批)
+- [ ] 实现企业微信通知
+- [ ] 添加工单搜索和筛选
+- [ ] 支持批量审批
+
+### 长期优化
+- [ ] 增加已审批工单历史
+- [ ] 添加审批统计图表
+- [ ] 支持工单评论功能
+- [ ] 集成到移动端
+
+---
+
+**创建时间**:2024-12-09
+
+**功能版本**:v1.0
+
+**设计规范**:iOS Design System

+ 352 - 0
docs/space-assignment-button-upgrade.md

@@ -0,0 +1,352 @@
+# 分配空间按钮升级说明
+
+## 📋 升级概述
+
+将设计师团队分配模态框中的小房子emoji图标(🏠)升级为精美的iOS风格按钮。
+
+---
+
+## 🎨 设计改进
+
+### Before(旧设计)
+```html
+<button class="space-assign-btn">
+  🏠
+</button>
+```
+
+**问题**:
+- ❌ 只有emoji图标,不够直观
+- ❌ 样式简单,缺乏视觉吸引力
+- ❌ 没有明确的文字说明
+- ❌ 无法体现iOS设计风格
+
+### After(新设计)
+```html
+<button class="space-assign-btn">
+  <svg class="btn-icon">...</svg>
+  <span class="btn-text">分配空间</span>
+</button>
+```
+
+**改进**:
+- ✅ 精美的SVG房子图标
+- ✅ 克莱茵蓝渐变背景
+- ✅ 明确的"分配空间"文字
+- ✅ 悬停光泽动画效果
+- ✅ 符合iOS设计规范
+
+---
+
+## 🎯 视觉特性
+
+### 1. 颜色设计
+```scss
+// 克莱茵蓝渐变(iOS风格)
+background: linear-gradient(135deg, #0047AB 0%, #4D91F7 100%);
+
+// 悬停时更亮
+background: linear-gradient(135deg, #0052C9 0%, #5BA0FF 100%);
+```
+
+**配色方案**:
+- **主色**:克莱茵蓝 (#0047AB) - iOS经典蓝
+- **辅色**:天蓝 (#4D91F7) - 渐变过渡
+- **悬停**:更亮的蓝色系
+
+### 2. 图标设计
+```html
+<svg class="btn-icon" viewBox="0 0 24 24">
+  <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
+  <polyline points="9 22 9 12 15 12 15 22"></polyline>
+</svg>
+```
+
+**图标特点**:
+- 16×16px 大小
+- 线条风格(stroke)
+- 2.5px 描边宽度
+- 轻微阴影效果
+
+### 3. 布局设计
+```scss
+display: inline-flex;
+align-items: center;
+gap: 6px; // 图标和文字间距
+padding: 8px 14px;
+border-radius: 8px;
+```
+
+**尺寸规格**:
+- **内边距**:8px(上下)× 14px(左右)
+- **圆角**:8px(iOS风格圆角)
+- **间距**:图标和文字间距6px
+- **字体**:13px,中等粗细(500)
+
+---
+
+## ✨ 交互效果
+
+### 1. 悬停效果(Hover)
+```scss
+&:hover {
+  background: linear-gradient(135deg, #0052C9 0%, #5BA0FF 100%);
+  transform: translateY(-2px) scale(1.02);
+  box-shadow: 0 4px 16px rgba(0, 71, 171, 0.35);
+}
+```
+
+**效果**:
+- 向上浮起 2px
+- 轻微放大 1.02倍
+- 阴影扩大加深
+- 背景渐变变亮
+
+### 2. 点击效果(Active)
+```scss
+&:active {
+  transform: translateY(0) scale(0.98);
+  box-shadow: 0 2px 8px rgba(0, 71, 171, 0.25);
+}
+```
+
+**效果**:
+- 回到原位
+- 轻微缩小 0.98倍
+- 阴影缩小变浅
+- 按下反馈明显
+
+### 3. 光泽动画
+```scss
+&::before {
+  content: '';
+  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+  left: -100%;
+  transition: left 0.5s ease;
+}
+
+&:hover::before {
+  left: 100%; // 从左滑到右
+}
+```
+
+**效果**:
+- 悬停时白色光泽从左向右滑过
+- 0.5秒平滑过渡
+- 半透明叠加效果
+
+### 4. 禁用状态
+```scss
+&:disabled {
+  background: linear-gradient(135deg, #94a3b8 0%, #cbd5e1 100%);
+  cursor: not-allowed;
+  box-shadow: none;
+}
+```
+
+**效果**:
+- 灰色渐变
+- 禁用鼠标样式
+- 无悬停效果
+
+---
+
+## 📊 对比表
+
+| 特性 | Before | After | 改进 |
+|------|--------|-------|------|
+| **图标** | emoji 🏠 | SVG 线条图标 | ✅ 更精致 |
+| **文字** | 无 | "分配空间" | ✅ 更明确 |
+| **背景** | 白色 | 克莱茵蓝渐变 | ✅ 更醒目 |
+| **阴影** | 无 | 立体阴影 | ✅ 更立体 |
+| **悬停** | 简单缩放 | 浮起+光泽 | ✅ 更生动 |
+| **点击** | 无反馈 | 按下效果 | ✅ 更交互 |
+| **动画** | 0.2s | 0.3s cubic-bezier | ✅ 更流畅 |
+
+---
+
+## 🎨 设计理念
+
+### iOS设计原则
+1. **扁平化**:无过多装饰,简洁清爽
+2. **渐变**:克莱茵蓝渐变,符合iOS蓝色系
+3. **圆角**:8px圆角,符合iOS圆润风格
+4. **阴影**:轻柔阴影,营造层次感
+5. **动画**:流畅自然,使用贝塞尔曲线
+
+### 视觉层次
+```
+┌─────────────────┐
+│  [SVG图标] 文字  │  ← 按钮内容(白色)
+└─────────────────┘
+        ↓
+   蓝色渐变背景
+        ↓
+     立体阴影
+```
+
+---
+
+## 📝 修改文件
+
+### 1. HTML模板
+**文件**:`designer-team-assignment-modal.component.html`
+
+**修改位置**:3处
+- Line 138-150(主设计师卡片)
+- Line 251-263(组内设计师卡片)
+- Line 339-355(跨组协作者卡片)
+
+**修改内容**:
+```html
+<!-- Before -->
+🏠
+
+<!-- After -->
+<svg class="btn-icon">...</svg>
+<span class="btn-text">分配空间</span>
+```
+
+### 2. SCSS样式
+**文件**:`designer-team-assignment-modal.component.scss`
+
+**修改位置**:Line 677-770
+
+**新增样式**:
+- 克莱茵蓝渐变背景
+- SVG图标样式
+- 文字样式
+- 悬停/点击效果
+- 光泽动画
+- 禁用状态
+
+---
+
+## 🧪 测试建议
+
+### 桌面端测试
+- [ ] 按钮显示正常(图标+文字)
+- [ ] 渐变背景正确
+- [ ] 悬停时浮起效果
+- [ ] 悬停时光泽动画
+- [ ] 点击时按下效果
+- [ ] 阴影效果正确
+
+### 移动端测试
+- [ ] 按钮触摸区域足够大
+- [ ] 响应速度快
+- [ ] 动画流畅不卡顿
+
+### 兼容性测试
+- [ ] Chrome(现代浏览器)
+- [ ] Safari(iOS设备)
+- [ ] Edge(Windows设备)
+
+---
+
+## 🎯 使用场景
+
+### 何时显示
+```typescript
+@if (enableSpaceAssignment && spaceScenes.length > 0) {
+  <button class="space-assign-btn">...</button>
+}
+```
+
+**条件**:
+1. `enableSpaceAssignment` = true(启用空间分配功能)
+2. `spaceScenes.length > 0`(存在可分配的空间)
+
+### 点击行为
+```typescript
+(click)="$event.stopPropagation(); openSpaceAssignment(designer)"
+```
+
+**动作**:
+1. 阻止事件冒泡
+2. 打开空间分配弹窗
+3. 传入设计师信息
+
+---
+
+## 🌟 亮点总结
+
+### 1. 视觉升级
+- ⭐ iOS风格克莱茵蓝渐变
+- ⭐ 精美的SVG线条图标
+- ⭐ 明确的"分配空间"文字
+
+### 2. 交互优化
+- ⭐ 悬停浮起效果
+- ⭐ 光泽滑动动画
+- ⭐ 点击按下反馈
+- ⭐ 流畅的贝塞尔曲线
+
+### 3. 细节打磨
+- ⭐ 图标和文字阴影
+- ⭐ 立体的按钮阴影
+- ⭐ 禁用状态样式
+- ⭐ 完美的间距比例
+
+---
+
+## 📸 效果预览
+
+### 正常状态
+```
+┌────────────────────┐
+│ [🏠图标] 分配空间   │  ← 克莱茵蓝渐变
+└────────────────────┘
+      ↓ 轻微阴影
+```
+
+### 悬停状态
+```
+┌────────────────────┐
+│ [🏠图标] 分配空间   │  ← 向上浮起 2px
+└────────────────────┘  ← 光泽从左滑到右
+      ↓ 阴影加深扩大
+```
+
+### 点击状态
+```
+┌────────────────────┐
+│ [🏠图标] 分配空间   │  ← 轻微缩小
+└────────────────────┘
+      ↓ 阴影缩小
+```
+
+---
+
+## 🔧 维护建议
+
+### 颜色调整
+如需调整颜色,修改渐变色值:
+```scss
+background: linear-gradient(135deg, #YOUR_COLOR_1 0%, #YOUR_COLOR_2 100%);
+```
+
+### 尺寸调整
+如需调整大小,修改以下参数:
+```scss
+padding: 8px 14px;      // 内边距
+font-size: 13px;        // 文字大小
+.btn-icon {
+  width: 16px;          // 图标大小
+  height: 16px;
+}
+```
+
+### 动画调整
+如需调整动画速度:
+```scss
+transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); // 0.3s改为你需要的时间
+```
+
+---
+
+**创建时间**:2024-12-09
+
+**升级版本**:v2.0
+
+**设计师**:iOS风格设计系统

+ 182 - 0
src/app/pages/designer/dashboard/dashboard.html

@@ -168,6 +168,65 @@
           </div>
         </div>
 
+        <!-- 改图任务审批区域 -->
+        <div class="info-column revision-tasks-column" *ngIf="pendingRevisionTasks.length > 0">
+          <div class="section-header">
+            <h2>
+              <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: middle; margin-right: 8px;">
+                <path d="M9 11H7v2h2v-2m4 0h-2v2h2v-2m4 0h-2v2h2v-2m2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2m0 16H5V9h14v11z"/>
+              </svg>
+              改图任务 ({{ pendingRevisionTasks.length }})
+            </h2>
+          </div>
+          
+          <div class="revision-task-list">
+            <div *ngFor="let task of pendingRevisionTasks" class="revision-task-card">
+              <div class="task-header">
+                <span class="task-id">#{{ task.id?.substring(0, 8) }}</span>
+                <span class="task-badge major">大修改</span>
+              </div>
+              
+              <div class="task-project" (click)="goToRevisionProject(task)">
+                <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
+                  <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
+                </svg>
+                {{ task.projectName }}
+              </div>
+              
+              <div class="task-spaces" *ngIf="task.spaceNames && task.spaceNames.length > 0">
+                <label>涉及空间:</label>
+                <div class="space-tags">
+                  <span *ngFor="let space of task.spaceNames" class="space-tag">{{ space }}</span>
+                </div>
+              </div>
+              
+              <div class="task-description">
+                <label>修改内容:</label>
+                <p>{{ task.description }}</p>
+              </div>
+              
+              <div class="task-meta">
+                <span class="task-creator">
+                  <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
+                    <path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
+                  </svg>
+                  {{ task.createdByName }}
+                </span>
+                <span class="task-time">{{ formatRevisionTime(task.createdAt) }}</span>
+              </div>
+              
+              <div class="task-actions">
+                <button class="btn-approve" (click)="openRevisionApprovalModal(task)">
+                  <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
+                    <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
+                  </svg>
+                  审批
+                </button>
+              </div>
+            </div>
+          </div>
+        </div>
+
         <!-- 代班信息区域 -->
         <div class="info-column" *ngIf="shiftTasks.length > 0">
           <div class="section-header">
@@ -396,4 +455,127 @@
       </div>
     </div>
   }
+  
+  <!-- 改图工单审批弹窗 -->
+  @if (showRevisionApprovalModal && currentRevisionTask) {
+    <div class="modal-overlay" (click)="closeRevisionApprovalModal()">
+      <div class="revision-approval-modal" (click)="$event.stopPropagation()">
+        <div class="modal-header">
+          <h3>
+            <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" style="vertical-align: middle; margin-right: 8px;">
+              <path d="M9 11H7v2h2v-2m4 0h-2v2h2v-2m4 0h-2v2h2v-2m2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2m0 16H5V9h14v11z"/>
+            </svg>
+            审批改图工单
+          </h3>
+          <button class="close-btn" (click)="closeRevisionApprovalModal()">×</button>
+        </div>
+        
+        <div class="modal-body">
+          <!-- 工单信息 -->
+          <div class="task-info-section">
+            <div class="info-row">
+              <label>工单编号:</label>
+              <span>#{{ currentRevisionTask.id?.substring(0, 8) }}</span>
+            </div>
+            <div class="info-row">
+              <label>项目名称:</label>
+              <span>{{ currentRevisionTask.projectName }}</span>
+            </div>
+            <div class="info-row">
+              <label>创建人:</label>
+              <span>{{ currentRevisionTask.createdByName }} ({{ currentRevisionTask.createdByRole }})</span>
+            </div>
+            <div class="info-row">
+              <label>创建时间:</label>
+              <span>{{ formatRevisionTime(currentRevisionTask.createdAt) }}</span>
+            </div>
+            <div class="info-row" *ngIf="currentRevisionTask.spaceNames && currentRevisionTask.spaceNames.length > 0">
+              <label>涉及空间:</label>
+              <div class="space-tags-inline">
+                <span *ngFor="let space of currentRevisionTask.spaceNames" class="space-tag-small">{{ space }}</span>
+              </div>
+            </div>
+            <div class="info-row">
+              <label>预计时间:</label>
+              <span>{{ currentRevisionTask.estimatedDays }}</span>
+            </div>
+            <div class="info-row description-row">
+              <label>修改内容:</label>
+              <p class="description-text">{{ currentRevisionTask.description }}</p>
+            </div>
+          </div>
+          
+          <!-- 审批操作 -->
+          <div class="approval-action-section">
+            <div class="action-type-selector">
+              <label class="radio-label" [class.active]="revisionApprovalForm.action === 'approve'">
+                <input 
+                  type="radio" 
+                  name="action" 
+                  value="approve" 
+                  [(ngModel)]="revisionApprovalForm.action"
+                >
+                <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
+                  <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
+                </svg>
+                <span>通过审批</span>
+              </label>
+              
+              <label class="radio-label" [class.active]="revisionApprovalForm.action === 'reject'">
+                <input 
+                  type="radio" 
+                  name="action" 
+                  value="reject" 
+                  [(ngModel)]="revisionApprovalForm.action"
+                >
+                <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
+                  <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 12z"/>
+                </svg>
+                <span>驳回</span>
+              </label>
+            </div>
+            
+            <!-- 通过审批的备注 -->
+            <div class="form-group" *ngIf="revisionApprovalForm.action === 'approve'">
+              <label>审批备注(可选)</label>
+              <textarea 
+                [(ngModel)]="revisionApprovalForm.notes"
+                placeholder="可以添加一些审批意见或建议..."
+                rows="3"
+                class="form-control"
+              ></textarea>
+            </div>
+            
+            <!-- 驳回原因 -->
+            <div class="form-group" *ngIf="revisionApprovalForm.action === 'reject'">
+              <label>驳回原因 <span class="required">*</span></label>
+              <textarea 
+                [(ngModel)]="revisionApprovalForm.rejectionReason"
+                placeholder="请说明驳回的原因..."
+                rows="4"
+                class="form-control"
+              ></textarea>
+            </div>
+          </div>
+        </div>
+        
+        <div class="modal-footer">
+          <button class="btn-cancel" (click)="closeRevisionApprovalModal()">
+            取消
+          </button>
+          <button 
+            class="btn-submit" 
+            [class.btn-approve]="revisionApprovalForm.action === 'approve'"
+            [class.btn-reject]="revisionApprovalForm.action === 'reject'"
+            (click)="submitRevisionApproval()">
+            <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
+              <path *ngIf="revisionApprovalForm.action === 'approve'" d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
+              <path *ngIf="revisionApprovalForm.action === 'reject'" 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 12z"/>
+            </svg>
+            {{ revisionApprovalForm.action === 'approve' ? '确认通过' : '确认驳回' }}
+          </button>
+        </div>
+      </div>
+    </div>
+  }
 </div>

+ 490 - 0
src/app/pages/designer/dashboard/dashboard.scss

@@ -3359,3 +3359,493 @@
     width: 100%;
   }
 }
+
+// 🆕 改图任务审批样式
+.revision-tasks-column {
+  background: linear-gradient(135deg, #fff5f0 0%, #ffffff 100%);
+  border-left: 4px solid #FF6B35 !important;
+  
+  .section-header h2 {
+    color: #FF6B35;
+    display: flex;
+    align-items: center;
+  }
+}
+
+.revision-task-list {
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+.revision-task-card {
+  background: #ffffff;
+  border-radius: 12px;
+  padding: 16px;
+  border: 1px solid #ffe4d6;
+  box-shadow: 0 2px 8px rgba(255, 107, 53, 0.1);
+  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+  
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 16px rgba(255, 107, 53, 0.15);
+  }
+  
+  &:last-child {
+    margin-bottom: 0;
+  }
+  
+  .task-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 12px;
+    
+    .task-id {
+      font-family: 'Courier New', monospace;
+      color: #666;
+      font-size: 12px;
+      font-weight: 600;
+    }
+    
+    .task-badge.major {
+      background: linear-gradient(135deg, #FF6B35 0%, #FF8F5C 100%);
+      color: white;
+      padding: 4px 12px;
+      border-radius: 12px;
+      font-size: 12px;
+      font-weight: 600;
+      box-shadow: 0 2px 6px rgba(255, 107, 53, 0.3);
+    }
+  }
+  
+  .task-project {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+    color: #0047AB;
+    font-weight: 500;
+    font-size: 14px;
+    margin-bottom: 12px;
+    cursor: pointer;
+    transition: all 0.2s ease;
+    
+    &:hover {
+      color: #0052C9;
+      text-decoration: underline;
+    }
+    
+    svg {
+      flex-shrink: 0;
+      opacity: 0.8;
+    }
+  }
+  
+  .task-spaces {
+    margin-bottom: 12px;
+    
+    label {
+      font-size: 12px;
+      color: #666;
+      font-weight: 600;
+      margin-bottom: 6px;
+      display: block;
+    }
+    
+    .space-tags {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 6px;
+      
+      .space-tag {
+        background: #e8f4ff;
+        color: #0047AB;
+        padding: 4px 10px;
+        border-radius: 8px;
+        font-size: 12px;
+        font-weight: 500;
+        border: 1px solid #d0e7ff;
+      }
+    }
+  }
+  
+  .task-description {
+    margin-bottom: 12px;
+    
+    label {
+      font-size: 12px;
+      color: #666;
+      font-weight: 600;
+      margin-bottom: 4px;
+      display: block;
+    }
+    
+    p {
+      font-size: 14px;
+      color: #333;
+      margin: 0;
+      line-height: 1.6;
+      max-height: 60px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      -webkit-line-clamp: 3;
+      line-clamp: 3;
+      -webkit-box-orient: vertical;
+    }
+  }
+  
+  .task-meta {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-size: 12px;
+    color: #999;
+    margin-bottom: 12px;
+    
+    .task-creator {
+      display: flex;
+      align-items: center;
+      gap: 4px;
+      
+      svg {
+        opacity: 0.6;
+      }
+    }
+    
+    .task-time {
+      font-style: italic;
+    }
+  }
+  
+  .task-actions {
+    display: flex;
+    justify-content: flex-end;
+    
+    .btn-approve {
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      background: linear-gradient(135deg, #34C759 0%, #4CD964 100%);
+      color: white;
+      border: none;
+      padding: 8px 16px;
+      border-radius: 8px;
+      font-size: 13px;
+      font-weight: 600;
+      cursor: pointer;
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      box-shadow: 0 2px 8px rgba(52, 199, 89, 0.25);
+      
+      svg {
+        flex-shrink: 0;
+      }
+      
+      &:hover {
+        background: linear-gradient(135deg, #30B350 0%, #43C65A 100%);
+        transform: translateY(-1px);
+        box-shadow: 0 4px 12px rgba(52, 199, 89, 0.35);
+      }
+      
+      &:active {
+        transform: translateY(0);
+        box-shadow: 0 2px 6px rgba(52, 199, 89, 0.25);
+      }
+    }
+  }
+}
+
+// 审批弹窗样式
+.revision-approval-modal {
+  background: white;
+  border-radius: 16px;
+  width: 600px;
+  max-width: 90vw;
+  max-height: 85vh;
+  overflow-y: auto;
+  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+  
+  .modal-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 20px 24px;
+    border-bottom: 1px solid #e5e5ea;
+    
+    h3 {
+      display: flex;
+      align-items: center;
+      font-size: 18px;
+      font-weight: 600;
+      margin: 0;
+      color: #333;
+    }
+    
+    .close-btn {
+      background: none;
+      border: none;
+      font-size: 28px;
+      line-height: 1;
+      cursor: pointer;
+      color: #999;
+      transition: color 0.2s ease;
+      padding: 0;
+      width: 32px;
+      height: 32px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      
+      &:hover {
+        color: #333;
+      }
+    }
+  }
+  
+  .modal-body {
+    padding: 24px;
+  }
+  
+  // 工单信息区
+  .task-info-section {
+    background: #f8f9fa;
+    border-radius: 12px;
+    padding: 16px;
+    margin-bottom: 24px;
+    
+    .info-row {
+      display: flex;
+      margin-bottom: 12px;
+      
+      &:last-child {
+        margin-bottom: 0;
+      }
+      
+      label {
+        font-weight: 600;
+        color: #666;
+        font-size: 13px;
+        min-width: 90px;
+        flex-shrink: 0;
+      }
+      
+      span {
+        color: #333;
+        font-size: 13px;
+      }
+    }
+    
+    .description-row {
+      flex-direction: column;
+      
+      .description-text {
+        margin-top: 8px;
+        line-height: 1.6;
+        color: #333;
+        font-size: 14px;
+      }
+    }
+  }
+  
+  .space-tags-inline {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 6px;
+    
+    .space-tag-small {
+      background: #e8f4ff;
+      color: #0047AB;
+      padding: 2px 8px;
+      border-radius: 6px;
+      font-size: 12px;
+      border: 1px solid #d0e7ff;
+    }
+  }
+  
+  // 审批操作区
+  .approval-action-section {
+    .action-type-selector {
+      display: grid;
+      grid-template-columns: 1fr 1fr;
+      gap: 12px;
+      margin-bottom: 20px;
+      
+      .radio-label {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        padding: 12px 16px;
+        border: 2px solid #e5e5ea;
+        border-radius: 12px;
+        cursor: pointer;
+        transition: all 0.2s ease;
+        background: white;
+        
+        input[type="radio"] {
+          display: none;
+        }
+        
+        svg {
+          flex-shrink: 0;
+        }
+        
+        span {
+          font-weight: 500;
+          font-size: 14px;
+        }
+        
+        &.active {
+          border-color: #34C759;
+          background: rgba(52, 199, 89, 0.05);
+          
+          &:last-child {
+            border-color: #FF3B30;
+            background: rgba(255, 59, 48, 0.05);
+          }
+        }
+        
+        &:hover {
+          border-color: #cbd5e1;
+        }
+      }
+    }
+    
+    .form-group {
+      label {
+        display: block;
+        font-weight: 600;
+        margin-bottom: 8px;
+        color: #333;
+        font-size: 14px;
+        
+        .required {
+          color: #FF3B30;
+        }
+      }
+      
+      textarea {
+        width: 100%;
+        padding: 12px;
+        border: 1px solid #e5e5ea;
+        border-radius: 8px;
+        font-size: 14px;
+        font-family: inherit;
+        resize: vertical;
+        line-height: 1.5;
+        transition: border-color 0.2s ease;
+        
+        &:focus {
+          outline: none;
+          border-color: #0047AB;
+        }
+        
+        &::placeholder {
+          color: #999;
+        }
+      }
+    }
+  }
+  
+  .modal-footer {
+    display: flex;
+    justify-content: flex-end;
+    gap: 12px;
+    padding: 20px 24px;
+    border-top: 1px solid #e5e5ea;
+    
+    button {
+      padding: 10px 24px;
+      border-radius: 8px;
+      font-size: 14px;
+      font-weight: 600;
+      cursor: pointer;
+      transition: all 0.2s ease;
+      border: none;
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      
+      svg {
+        flex-shrink: 0;
+      }
+    }
+    
+    .btn-cancel {
+      background: #f1f3f5;
+      color: #666;
+      
+      &:hover {
+        background: #e9ecef;
+      }
+    }
+    
+    .btn-submit {
+      color: white;
+      
+      &.btn-approve {
+        background: linear-gradient(135deg, #34C759 0%, #4CD964 100%);
+        box-shadow: 0 2px 8px rgba(52, 199, 89, 0.25);
+        
+        &:hover {
+          background: linear-gradient(135deg, #30B350 0%, #43C65A 100%);
+          box-shadow: 0 4px 12px rgba(52, 199, 89, 0.35);
+          transform: translateY(-1px);
+        }
+        
+        &:active {
+          transform: translateY(0);
+        }
+      }
+      
+      &.btn-reject {
+        background: linear-gradient(135deg, #FF3B30 0%, #FF5A4F 100%);
+        box-shadow: 0 2px 8px rgba(255, 59, 48, 0.25);
+        
+        &:hover {
+          background: linear-gradient(135deg, #E6352B 0%, #FF5046 100%);
+          box-shadow: 0 4px 12px rgba(255, 59, 48, 0.35);
+          transform: translateY(-1px);
+        }
+        
+        &:active {
+          transform: translateY(0);
+        }
+      }
+    }
+  }
+}
+
+// 响应式设计 - 改图任务
+@media (max-width: 768px) {
+  .revision-approval-modal {
+    width: 95vw;
+    max-height: 90vh;
+    
+    .modal-header {
+      padding: 16px 20px;
+      
+      h3 {
+        font-size: 16px;
+      }
+    }
+    
+    .modal-body {
+      padding: 20px;
+    }
+    
+    .approval-action-section {
+      .action-type-selector {
+        grid-template-columns: 1fr;
+      }
+    }
+    
+    .modal-footer {
+      padding: 16px 20px;
+      
+      button {
+        padding: 10px 20px;
+        font-size: 13px;
+      }
+    }
+  }
+}

+ 172 - 2
src/app/pages/designer/dashboard/dashboard.ts

@@ -12,6 +12,9 @@ import { LeaveService, LeaveApplication } from '../../../services/leave.service'
 import { FormsModule } from '@angular/forms';
 // 🆕 导入项目负载时间轴组件
 import { ProjectTimelineComponent } from '../../team-leader/project-timeline';
+// 🆕 导入改图工单服务和类型
+import { RevisionTaskService } from '../../services/revision-task.service';
+import type { RevisionTask } from '../../services/revision-task.service';
 
 interface ShiftTask {
   id: string;
@@ -60,6 +63,16 @@ export class Dashboard implements OnInit {
   feedbackProjectId: string = '';
   countdowns: Map<string, string> = new Map();
   
+  // 🆕 改图工单相关
+  pendingRevisionTasks: RevisionTask[] = []; // 待审批的改图工单
+  showRevisionApprovalModal: boolean = false; // 审批弹窗
+  currentRevisionTask: RevisionTask | null = null; // 当前审批的工单
+  revisionApprovalForm = {
+    action: 'approve' as 'approve' | 'reject',
+    notes: '',
+    rejectionReason: ''
+  };
+  
   // 代班信息相关属性
   shiftTasks: ShiftTask[] = [];
   
@@ -101,7 +114,8 @@ export class Dashboard implements OnInit {
     private route: ActivatedRoute,
     private router: Router,
     private taskService: DesignerTaskService,
-    private leaveService: LeaveService
+    private leaveService: LeaveService,
+    private revisionTaskService: RevisionTaskService
   ) {}
 
   async ngOnInit(): Promise<void> {
@@ -205,7 +219,8 @@ export class Dashboard implements OnInit {
         this.loadShiftTasks(),
         this.calculateWorkloadPercentage(),
         this.loadProjectTimeline(),
-        this.loadDesignerProjects() // 🆕 加载项目负载数据
+        this.loadDesignerProjects(), // 🆕 加载项目负载数据
+        this.loadPendingRevisionTasks() // 🆕 加载待审批改图工单
       ]);
       console.log('✅ 设计师仪表板数据加载完成');
     } catch (error) {
@@ -1457,5 +1472,160 @@ export class Dashboard implements OnInit {
     
   }
 
+  // ============ 改图工单相关方法 ============
+
+  /**
+   * 加载待审批的改图工单
+   */
+  private async loadPendingRevisionTasks(): Promise<void> {
+    try {
+      const Parse = await import('fmode-ng/parse').then(m => m.FmodeParse.with('nova'));
+      
+      // 查询所有项目的待审批改图工单
+      const projectQuery = new Parse.Query('Project');
+      projectQuery.exists('data.revisionTasks');
+      projectQuery.limit(1000);
+      
+      const projects = await projectQuery.find();
+      
+      this.pendingRevisionTasks = [];
+      
+      for (const project of projects) {
+        const data = project.get('data') || {};
+        const revisionTasks: RevisionTask[] = data.revisionTasks || [];
+        
+        // 筛选待审批的工单
+        const pendingTasks = revisionTasks.filter(
+          (task: RevisionTask) => task.status === 'pending_approval'
+        );
+        
+        // 添加项目名称
+        pendingTasks.forEach((task: any) => {
+          task.projectName = project.get('name') || '未命名项目';
+          task.projectId = project.id;
+        });
+        
+        this.pendingRevisionTasks.push(...pendingTasks);
+      }
+      
+      // 按创建时间倒序排列
+      this.pendingRevisionTasks.sort((a, b) => 
+        new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
+      );
+      
+      console.log(`✅ 加载到 ${this.pendingRevisionTasks.length} 个待审批改图工单`);
+      
+    } catch (error) {
+      console.error('❌ 加载待审批改图工单失败:', error);
+      this.pendingRevisionTasks = [];
+    }
+  }
+
+  /**
+   * 打开审批弹窗
+   */
+  openRevisionApprovalModal(task: RevisionTask): void {
+    this.currentRevisionTask = task;
+    this.revisionApprovalForm = {
+      action: 'approve',
+      notes: '',
+      rejectionReason: ''
+    };
+    this.showRevisionApprovalModal = true;
+  }
+
+  /**
+   * 关闭审批弹窗
+   */
+  closeRevisionApprovalModal(): void {
+    this.showRevisionApprovalModal = false;
+    this.currentRevisionTask = null;
+  }
+
+  /**
+   * 提交审批
+   */
+  async submitRevisionApproval(): Promise<void> {
+    if (!this.currentRevisionTask || !this.currentProfile) {
+      return;
+    }
+
+    try {
+      const approver = {
+        id: this.currentProfile.id,
+        name: this.currentProfile.get('name') || '组长'
+      };
+
+      if (this.revisionApprovalForm.action === 'approve') {
+        // 通过审批
+        await this.revisionTaskService.approveRevisionTask(
+          this.currentRevisionTask.projectId,
+          this.currentRevisionTask.id!,
+          approver,
+          this.revisionApprovalForm.notes
+        );
+        
+        window.fmode?.toast?.success?.('改图工单已通过审批');
+      } else {
+        // 驳回
+        if (!this.revisionApprovalForm.rejectionReason.trim()) {
+          window.fmode?.alert?.('请填写驳回原因');
+          return;
+        }
+        
+        await this.revisionTaskService.rejectRevisionTask(
+          this.currentRevisionTask.projectId,
+          this.currentRevisionTask.id!,
+          approver,
+          this.revisionApprovalForm.rejectionReason
+        );
+        
+        window.fmode?.toast?.success?.('改图工单已驳回');
+      }
+
+      // 关闭弹窗
+      this.closeRevisionApprovalModal();
+
+      // 重新加载待审批工单
+      await this.loadPendingRevisionTasks();
+
+    } catch (error) {
+      console.error('❌ 提交审批失败:', error);
+      window.fmode?.alert?.('提交审批失败,请重试');
+    }
+  }
+
+  /**
+   * 跳转到项目详情
+   */
+  goToRevisionProject(task: RevisionTask): void {
+    if (task.projectId && this.cid) {
+      this.router.navigate(['/wxwork', this.cid, 'project', task.projectId]);
+    }
+  }
+
+  /**
+   * 格式化时间显示
+   */
+  formatRevisionTime(date: Date): string {
+    const now = new Date();
+    const diff = now.getTime() - new Date(date).getTime();
+    const minutes = Math.floor(diff / 60000);
+    const hours = Math.floor(diff / 3600000);
+    const days = Math.floor(diff / 86400000);
+
+    if (minutes < 1) return '刚刚';
+    if (minutes < 60) return `${minutes}分钟前`;
+    if (hours < 24) return `${hours}小时前`;
+    if (days < 7) return `${days}天前`;
+    
+    return new Date(date).toLocaleDateString('zh-CN', {
+      month: '2-digit',
+      day: '2-digit',
+      hour: '2-digit',
+      minute: '2-digit'
+    });
+  }
+
 }
 

+ 15 - 3
src/app/pages/designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component.html

@@ -141,7 +141,11 @@
                           (click)="$event.stopPropagation(); openSpaceAssignment(designer)"
                           title="分配空间"
                         >
-                          🏠
+                          <svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                            <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
+                            <polyline points="9 22 9 12 15 12 15 22"></polyline>
+                          </svg>
+                          <span class="btn-text">分配空间</span>
                         </button>
                       }
                     </div>
@@ -250,7 +254,11 @@
                         (click)="$event.stopPropagation(); openSpaceAssignment(designer)"
                         title="分配空间"
                       >
-                        🏠
+                        <svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                          <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
+                          <polyline points="9 22 9 12 15 12 15 22"></polyline>
+                        </svg>
+                        <span class="btn-text">分配空间</span>
                       </button>
                     }
                   </div>
@@ -338,7 +346,11 @@
                             (click)="$event.stopPropagation(); openSpaceAssignment(designer)"
                             title="分配空间"
                           >
-                            🏠
+                            <svg class="btn-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                              <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
+                              <polyline points="9 22 9 12 15 12 15 22"></polyline>
+                            </svg>
+                            <span class="btn-text">分配空间</span>
                           </button>
                         }
                       </div>

+ 81 - 4
src/app/pages/designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component.scss

@@ -674,16 +674,15 @@
     align-items: center;
     gap: 6px; // 按钮之间的间距
 
-    .calendar-btn,
-    .space-assign-btn {
+    .calendar-btn {
       background: white;
       border: 1px solid #e2e8f0;
       padding: 6px 10px;
       cursor: pointer;
       border-radius: 6px;
-      font-size: 14px; // 从16px减少到14px
+      font-size: 14px;
       transition: all 0.2s ease;
-      white-space: nowrap; // 防止文字换行
+      white-space: nowrap;
 
       &:hover {
         background: #f8fafc;
@@ -691,6 +690,84 @@
         transform: scale(1.05);
       }
     }
+
+    .space-assign-btn {
+      display: inline-flex;
+      align-items: center;
+      gap: 6px;
+      background: linear-gradient(135deg, #0047AB 0%, #4D91F7 100%); // 克莱茵蓝渐变
+      color: white;
+      border: none;
+      padding: 8px 14px;
+      cursor: pointer;
+      border-radius: 8px;
+      font-size: 13px;
+      font-weight: 500;
+      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+      white-space: nowrap;
+      box-shadow: 0 2px 8px rgba(0, 71, 171, 0.2);
+      position: relative;
+      overflow: hidden;
+
+      // 光泽效果
+      &::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: -100%;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(
+          90deg,
+          transparent,
+          rgba(255, 255, 255, 0.2),
+          transparent
+        );
+        transition: left 0.5s ease;
+      }
+
+      .btn-icon {
+        width: 16px;
+        height: 16px;
+        flex-shrink: 0;
+        stroke-width: 2.5;
+        filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
+      }
+
+      .btn-text {
+        font-weight: 500;
+        letter-spacing: 0.3px;
+        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+      }
+
+      &:hover {
+        background: linear-gradient(135deg, #0052C9 0%, #5BA0FF 100%);
+        transform: translateY(-2px) scale(1.02);
+        box-shadow: 0 4px 16px rgba(0, 71, 171, 0.35);
+
+        &::before {
+          left: 100%;
+        }
+      }
+
+      &:active {
+        transform: translateY(0) scale(0.98);
+        box-shadow: 0 2px 8px rgba(0, 71, 171, 0.25);
+      }
+
+      // 禁用状态
+      &:disabled {
+        background: linear-gradient(135deg, #94a3b8 0%, #cbd5e1 100%);
+        cursor: not-allowed;
+        box-shadow: none;
+        transform: none;
+
+        &:hover {
+          transform: none;
+          box-shadow: none;
+        }
+      }
+    }
   }
 }
 

+ 1 - 0
src/app/pages/services/revision-task.service.ts

@@ -26,6 +26,7 @@ export type RevisionStatus =
 export interface RevisionTask {
   id?: string;
   projectId: string;
+  projectName: string;          // 项目名称(冗余存储)
   type: RevisionType;
   spaceIds: string[];           // 涉及的空间ID
   spaceNames: string[];         // 空间名称(冗余存储)

+ 2 - 0
src/modules/project/components/revision-task-modal/revision-task-modal.component.ts

@@ -152,6 +152,7 @@ interface SpaceOption {
 export class RevisionTaskModalComponent {
   @Input() visible: boolean = false;
   @Input() projectId: string = '';
+  @Input() projectName: string = ''; // 项目名称
   @Input() availableSpaces: SpaceOption[] = [];
   @Input() currentUser: any = null;
   
@@ -262,6 +263,7 @@ export class RevisionTaskModalComponent {
     try {
       const taskData: Omit<RevisionTask, 'id' | 'createdAt'> = {
         projectId: this.projectId,
+        projectName: this.projectName || '未命名项目',
         type: this.taskType,
         spaceIds: this.selectedSpaces.map(s => s.id),
         spaceNames: this.selectedSpaces.map(s => s.name),

+ 1 - 0
src/modules/project/pages/project-detail/stages/components/stage-delivery-execution/stage-delivery-execution.component.html

@@ -277,6 +277,7 @@
 <app-revision-task-modal
   [visible]="showRevisionTaskModal"
   [projectId]="project?.id || ''"
+  [projectName]="project?.get('name') || '未命名项目'"
   [availableSpaces]="cachedRevisionSpaces"
   [currentUser]="currentUser"
   (close)="closeRevisionTaskModal()"