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