在设计师组长Dashboard的紧急事件板块增加改图任务审批功能,显示待审批的大修改工单,并提供审批操作。
Dashboard位置:src/app/pages/designer/dashboard/
显示区域:附加信息区域(additional-info-section),位于"待处理反馈"和"代班信息"之间
pendingRevisionTasks.length > 0dashboard.ts)import { RevisionTaskService, RevisionTask } from '../../../services/revision-task.service';
// 改图工单相关
pendingRevisionTasks: RevisionTask[] = []; // 待审批的改图工单
showRevisionApprovalModal: boolean = false; // 审批弹窗
currentRevisionTask: RevisionTask | null = null; // 当前审批的工单
revisionApprovalForm = {
action: 'approve' as 'approve' | 'reject',
notes: '',
rejectionReason: ''
};
constructor(
// ... 其他服务
private revisionTaskService: RevisionTaskService
) {}
await Promise.all([
// ... 其他加载
this.loadPendingRevisionTasks() // 🆕 加载待审批改图工单
]);
1. 加载待审批工单
private async loadPendingRevisionTasks(): Promise<void> {
// 查询所有项目的revisionTasks
// 筛选status === 'pending_approval'
// 按创建时间倒序排列
}
2. 打开审批弹窗
openRevisionApprovalModal(task: RevisionTask): void {
this.currentRevisionTask = task;
this.revisionApprovalForm = {
action: 'approve',
notes: '',
rejectionReason: ''
};
this.showRevisionApprovalModal = true;
}
3. 提交审批
async submitRevisionApproval(): Promise<void> {
if (action === 'approve') {
await this.revisionTaskService.approveRevisionTask(...);
} else {
// 验证驳回原因
await this.revisionTaskService.rejectRevisionTask(...);
}
// 重新加载待审批工单
await this.loadPendingRevisionTasks();
}
4. 跳转到项目
goToRevisionProject(task: RevisionTask): void {
this.router.navigate(['/wxwork', this.cid, 'project', task.projectId]);
}
5. 格式化时间
formatRevisionTime(date: Date): string {
// 刚刚、X分钟前、X小时前、X天前
// 超过7天显示日期
}
dashboard.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>
<!-- 改图工单审批弹窗 -->
@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>
}
dashboard.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);
}
}
.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. 显示成功提示
┌────────────────────────────────────────┐
│ 改图任务 (2) │
├────────────────────────────────────────┤
│ ┌────────────────────────────────────┐ │
│ │ #RT_17... [大修改] │ │
│ │ │ │
│ │ 🏠 现代简约客厅设计项目 │ │
│ │ │ │
│ │ 涉及空间:[主卧] [次卧] [客厅] │ │
│ │ │ │
│ │ 修改内容:主卧的整体氛围需要调整 │ │
│ │ │ │
│ │ 👤 刘雨熙 18分钟前 │ │
│ │ │ │
│ │ [✓ 审批] │ │
│ └────────────────────────────────────┘ │
└────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 📅 审批改图工单 × │
├─────────────────────────────────────────┤
│ 工单编号:#RT_17652 │
│ 项目名称:现代简约客厅设计项目 │
│ 创建人:刘雨熙 (designer) │
│ 创建时间:18分钟前 │
│ 涉及空间:[主卧] [次卧] [客厅] │
│ 预计时间:2-3天 │
│ 修改内容:主卧的整体氛围需要调整... │
│ │
│ ┌───────────┐ ┌──────────┐ │
│ │ ✓ 通过审批 │ │ × 驳回 │ │
│ └───────────┘ └──────────┘ │
│ │
│ 审批备注(可选): │
│ ┌─────────────────────────────────┐ │
│ │ 同意修改,请尽快完成... │ │
│ └─────────────────────────────────┘ │
│ │
│ [取消] [✓ 确认通过] │
└─────────────────────────────────────────┘
创建时间:2024-12-09
功能版本:v1.0
设计规范:iOS Design System