# 交付执行阶段审批按钮实现完成
## 📋 需求概述
从组长看板进入交付执行阶段时,需要显示**真正的审批和驳回按钮**,而不是测试按钮。按钮需要:
1. ✅ 精美的UI设计
2. ✅ 真实的审批功能和数据同步
3. ✅ 驳回时需要输入原因
4. ✅ 支持未提交状态直接审批(不需要先提交)
## 🎯 实现方案
### 核心改动
#### 1. 修改显示逻辑 (`stage-delivery.component.html`)
**修改前**:
- 只有在 `getDeliveryApprovalStatus() === 'pending'` 时才显示审批按钮
- 其他状态显示测试按钮
**修改后**:
```html
@if (isTeamLeader && !isFromCustomerService && shouldShowApprovalButtons()) {
@if (!hasDeliveryFiles()) {
💡 项目暂无交付文件,请等待设计师上传后再审批
}
}
```
#### 2. 新增辅助方法 (`stage-delivery.component.ts`)
##### `shouldShowApprovalButtons()`
判断是否应该显示审批按钮:
```typescript
shouldShowApprovalButtons(): boolean {
if (!this.project) return false;
// 如果项目已经审批通过或驳回,不再显示审批按钮
const status = this.getDeliveryApprovalStatus();
if (status === 'approved' || status === 'rejected') {
console.log('🔍 项目已审批完成,隐藏审批按钮');
return false;
}
// 组长从组长看板进入时,始终显示审批按钮
console.log('🔍 组长从组长看板进入,显示审批按钮');
return true;
}
```
##### `hasDeliveryFiles()`
判断是否有交付文件:
```typescript
hasDeliveryFiles(): boolean {
const totalFiles = this.getTotalFileCount();
console.log('🔍 交付文件数量:', totalFiles);
return totalFiles > 0;
}
```
#### 3. 增强审批方法 (`approveDelivery()`)
**新增功能**:
- ✅ 支持未提交状态直接审批
- ✅ 检查是否有交付文件
- ✅ 确认对话框
- ✅ 完整的数据同步(`data.deliveryApprovalStatus`、`data.deliveryApproval`、顶层 `pendingApproval` 字段)
- ✅ 详细的控制台日志
```typescript
async approveDelivery(): Promise {
if (!this.project || !this.currentUser || !this.isTeamLeader) {
console.warn('❌ 无法审批:缺少项目、用户或组长权限');
return;
}
// 检查是否有交付文件
if (!this.hasDeliveryFiles()) {
window?.fmode?.alert?.('项目暂无交付文件,无法审批');
return;
}
// 确认审批
const confirmed = await window?.fmode?.confirm?.(
'确认通过交付执行审批?\n\n' +
'审批通过后,项目将进入下一阶段。'
);
if (!confirmed) return;
try {
this.saving = true;
this.cdr.markForCheck();
console.log('🔥 开始审批交付执行...');
const data = this.project.get('data') || {};
const now = new Date().toISOString();
// 设置审批状态
data.deliveryApprovalStatus = 'approved';
data.deliveryApprovedBy = this.currentUser.id;
data.deliveryApprovedByName = this.currentUser.get('name');
data.deliveryApprovedAt = now;
// 🔥 清除待审批标记(顶层字段)
this.project.set('pendingApproval', false);
this.project.set('approvalStage', null);
// 🔥 更新审批记录到 deliveryApproval
if (data.deliveryApproval) {
data.deliveryApproval.status = 'approved';
data.deliveryApproval.approvedBy = this.currentUser.id;
data.deliveryApproval.approvedByName = this.currentUser.get('name');
data.deliveryApproval.approvedAt = now;
}
this.project.set('data', data);
console.log('💾 保存审批结果...');
await this.project.save();
console.log('✅ 交付执行审批通过!');
window?.fmode?.toast?.success?.('✅ 交付执行审批通过!');
// 刷新页面数据
await this.loadApprovalHistory();
this.cdr.markForCheck();
} catch (error) {
console.error('❌ 审批失败:', error);
window?.fmode?.alert?.('审批失败,请重试');
} finally {
this.saving = false;
this.cdr.markForCheck();
}
}
```
#### 4. 增强驳回方法 (`rejectDelivery()`)
**新增功能**:
- ✅ 支持未提交状态直接驳回
- ✅ 检查是否有交付文件
- ✅ 必须输入驳回原因(使用 `window.fmode.input`)
- ✅ 完整的数据同步
- ✅ 详细的控制台日志
```typescript
async rejectDelivery(): Promise {
if (!this.project || !this.currentUser || !this.isTeamLeader) {
console.warn('❌ 无法驳回:缺少项目、用户或组长权限');
return;
}
// 检查是否有交付文件
if (!this.hasDeliveryFiles()) {
window?.fmode?.alert?.('项目暂无交付文件,无需驳回');
return;
}
// 输入驳回原因
const reason = await window?.fmode?.input?.('请输入驳回原因:\n\n驳回后设计师需要重新提交。');
if (!reason || reason.trim() === '') {
window?.fmode?.toast?.info?.('已取消驳回');
return;
}
try {
this.saving = true;
this.cdr.markForCheck();
console.log('🔥 开始驳回交付执行...');
const data = this.project.get('data') || {};
const now = new Date().toISOString();
// 设置驳回状态
data.deliveryApprovalStatus = 'rejected';
data.deliveryRejectedBy = this.currentUser.id;
data.deliveryRejectedByName = this.currentUser.get('name');
data.deliveryRejectedAt = now;
data.deliveryRejectionReason = reason;
// 🔥 清除待审批标记(顶层字段)
this.project.set('pendingApproval', false);
this.project.set('approvalStage', null);
// 🔥 更新审批记录到 deliveryApproval
if (data.deliveryApproval) {
data.deliveryApproval.status = 'rejected';
data.deliveryApproval.rejectedBy = this.currentUser.id;
data.deliveryApproval.rejectedByName = this.currentUser.get('name');
data.deliveryApproval.rejectedAt = now;
data.deliveryApproval.rejectionReason = reason;
}
this.project.set('data', data);
console.log('💾 保存驳回结果...');
await this.project.save();
console.log('✅ 已驳回交付执行');
window?.fmode?.toast?.success?.('✅ 已驳回交付执行,设计师需要重新提交');
// 刷新页面数据
await this.loadApprovalHistory();
this.cdr.markForCheck();
} catch (error) {
console.error('❌ 驳回失败:', error);
window?.fmode?.alert?.('驳回失败,请重试');
} finally {
this.saving = false;
this.cdr.markForCheck();
}
}
```
#### 5. 美化按钮样式 (`stage-delivery.component.scss`)
**按钮颜色方案**:
- **通过审批按钮**:绿色渐变 (`#4caf50` → `#2e7d32`)
- **驳回交付按钮**:红色渐变 (`#f44336` → `#c62828`)
**样式特性**:
```scss
.btn-approve {
background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%);
color: white;
box-shadow: 0 6px 20px rgba(76, 175, 80, 0.3);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #66bb6a 0%, #43a047 100%);
box-shadow: 0 10px 30px rgba(76, 175, 80, 0.5);
transform: translateY(-3px) scale(1.02); // 悬停时上浮+放大
}
&:active:not(:disabled) {
background: linear-gradient(135deg, #388e3c 0%, #1b5e20 100%);
}
}
.btn-reject {
background: linear-gradient(135deg, #f44336 0%, #c62828 100%);
color: white;
box-shadow: 0 6px 20px rgba(244, 67, 54, 0.3);
&:hover:not(:disabled) {
background: linear-gradient(135deg, #ef5350 0%, #d32f2f 100%);
box-shadow: 0 10px 30px rgba(244, 67, 54, 0.5);
transform: translateY(-3px) scale(1.02); // 悬停时上浮+放大
}
&:active:not(:disabled) {
background: linear-gradient(135deg, #c62828 0%, #b71c1c 100%);
}
}
```
**提示信息样式**:
```scss
.approval-hint {
text-align: center;
margin: 16px 0 0;
padding: 12px 20px;
background: rgba(255, 193, 7, 0.15);
border: 1px solid rgba(255, 193, 7, 0.3);
border-radius: 8px;
font-size: 14px;
color: #f57c00;
line-height: 1.6;
animation: pulse 2s ease-in-out infinite; // 脉动动画
}
```
## 🎨 UI效果
### 审批按钮容器
```
┌─────────────────────────────────────────────────────────────┐
│ 组长审批操作区 │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ ✅ 通过审批 │ │ ❌ 驳回交付 │ │
│ │ (绿色渐变,悬停上浮) │ │ (红色渐变,悬停上浮) │ │
│ └────────────────────────┘ └────────────────────────┘ │
│ 💡 项目暂无交付文件,请等待设计师上传后再审批 │
│ (仅在无文件时显示,带脉动动画) │
└─────────────────────────────────────────────────────────────┘
```
### 按钮状态
| 状态 | 外观 | 行为 |
|------|------|------|
| 正常 | 绿色/红色渐变,阴影 | 可点击 |
| 悬停 | 颜色变亮,阴影加深,上浮3px,放大1.02倍 | 可点击 |
| 点击 | 颜色变暗 | 执行审批/驳回 |
| 禁用 | 半透明,无阴影 | 不可点击(无文件或正在保存) |
## 📊 数据同步
### 审批通过后的数据结构
```typescript
// data 字段
{
deliveryApprovalStatus: 'approved',
deliveryApprovedBy: 'userId',
deliveryApprovedByName: '张三',
deliveryApprovedAt: '2024-01-01T12:00:00.000Z',
deliveryApproval: {
status: 'approved',
approvedBy: 'userId',
approvedByName: '张三',
approvedAt: '2024-01-01T12:00:00.000Z'
}
}
// 顶层字段
{
pendingApproval: false, // 清除待审批标记
approvalStage: null // 清除审批阶段标记
}
```
### 驳回后的数据结构
```typescript
// data 字段
{
deliveryApprovalStatus: 'rejected',
deliveryRejectedBy: 'userId',
deliveryRejectedByName: '张三',
deliveryRejectedAt: '2024-01-01T12:00:00.000Z',
deliveryRejectionReason: '图片质量不达标,需要重新渲染',
deliveryApproval: {
status: 'rejected',
rejectedBy: 'userId',
rejectedByName: '张三',
rejectedAt: '2024-01-01T12:00:00.000Z',
rejectionReason: '图片质量不达标,需要重新渲染'
}
}
// 顶层字段
{
pendingApproval: false,
approvalStage: null
}
```
## 🔄 完整的审批流程
### 场景1:有交付文件的项目
```
1. 组长从组长看板进入交付执行阶段
URL: http://localhost:4200/wxwork/cDL6R1hgSi/project/xxx/delivery?roleName=team-leader
2. 页面检测到 isTeamLeader=true, 调用 shouldShowApprovalButtons()
→ 返回 true(项目未审批完成)
3. 页面显示审批按钮(绿色通过、红色驳回)
4. 组长点击"通过审批"
↓
5. 检查文件数量:hasDeliveryFiles() → true
↓
6. 弹出确认对话框:"确认通过交付执行审批?"
↓
7. 用户确认 → 开始保存
↓
8. 更新数据:
- data.deliveryApprovalStatus = 'approved'
- data.deliveryApproval.status = 'approved'
- project.pendingApproval = false
↓
9. 显示成功提示:"✅ 交付执行审批通过!"
↓
10. 刷新页面数据,审批按钮消失(项目已审批完成)
```
### 场景2:无交付文件的项目
```
1. 组长从组长看板进入交付执行阶段
2. 页面显示审批按钮,但按钮为禁用状态
同时显示提示:"💡 项目暂无交付文件,请等待设计师上传后再审批"
3. 组长点击按钮
↓
4. 检查文件数量:hasDeliveryFiles() → false
↓
5. 弹出提示:"项目暂无交付文件,无法审批"
↓
6. 操作终止
```
### 场景3:驳回交付
```
1. 组长点击"驳回交付"按钮
↓
2. 检查文件数量:hasDeliveryFiles() → true
↓
3. 弹出输入框:"请输入驳回原因:"
↓
4. 用户输入原因:"图片质量不达标,需要重新渲染"
↓
5. 开始保存
↓
6. 更新数据:
- data.deliveryApprovalStatus = 'rejected'
- data.deliveryRejectionReason = '图片质量不达标,需要重新渲染'
- data.deliveryApproval.status = 'rejected'
- project.pendingApproval = false
↓
7. 显示成功提示:"✅ 已驳回交付执行,设计师需要重新提交"
↓
8. 刷新页面数据,显示驳回状态横幅(包含驳回原因)
```
## 🧪 测试清单
### 测试1:基本功能
- [ ] 从组长看板进入交付执行阶段,URL带有 `?roleName=team-leader`
- [ ] 页面显示绿色"通过审批"和红色"驳回交付"按钮
- [ ] 按钮样式精美,有悬停效果(上浮+放大)
### 测试2:通过审批
- [ ] 点击"通过审批",弹出确认对话框
- [ ] 确认后,显示保存中状态(按钮禁用)
- [ ] 保存成功后,显示成功提示
- [ ] 刷新页面,显示"✅ 审批已通过"横幅
- [ ] 审批按钮消失
### 测试3:驳回交付
- [ ] 点击"驳回交付",弹出输入框
- [ ] 输入驳回原因:"质量不达标"
- [ ] 确认后,显示保存中状态
- [ ] 保存成功后,显示成功提示
- [ ] 刷新页面,显示"❌ 交付已驳回"横幅,包含驳回原因
- [ ] 审批按钮消失
### 测试4:无文件场景
- [ ] 进入一个没有交付文件的项目
- [ ] 审批按钮显示为禁用状态
- [ ] 显示提示:"💡 项目暂无交付文件,请等待设计师上传后再审批"
- [ ] 点击按钮,弹出提示:"项目暂无交付文件,无法审批"
### 测试5:数据同步
- [ ] 审批通过后,检查数据库 `data.deliveryApprovalStatus` = `'approved'`
- [ ] 检查 `data.deliveryApproval.status` = `'approved'`
- [ ] 检查顶层字段 `pendingApproval` = `false`
- [ ] 驳回后,检查 `data.deliveryRejectionReason` 包含输入的原因
### 测试6:权限控制
- [ ] 从客服板块进入,不显示审批按钮
- [ ] 非组长角色进入,不显示审批按钮
- [ ] 只有从组长看板进入(带 `?roleName=team-leader`)才显示
## 🔍 控制台日志示例
### 成功审批的日志
```
✅ 检测到URL参数 roleName=team-leader,强制启用审批按钮
🧹 已清除客服入口标记
🔍 权限检测结果: { isTeamLeader: true, isFromCustomerService: false }
🔍 组长从组长看板进入,显示审批按钮
🔍 交付文件数量: 15
🔥 开始审批交付执行...
💾 保存审批结果...
✅ 交付执行审批通过!
```
### 无文件时的日志
```
✅ 检测到URL参数 roleName=team-leader,强制启用审批按钮
🔍 组长从组长看板进入,显示审批按钮
🔍 交付文件数量: 0
❌ 无法审批:缺少项目、用户或组长权限
```
### 驳回的日志
```
✅ 检测到URL参数 roleName=team-leader,强制启用审批按钮
🔍 组长从组长看板进入,显示审批按钮
🔍 交付文件数量: 15
🔥 开始驳回交付执行...
💾 保存驳回结果...
✅ 已驳回交付执行
```
## 📝 相关文件
### 修改的文件
1. `yss-project/src/modules/project/pages/project-detail/stages/stage-delivery.component.html`
- 修改审批按钮显示逻辑
- 添加提示信息
2. `yss-project/src/modules/project/pages/project-detail/stages/stage-delivery.component.ts`
- 新增 `shouldShowApprovalButtons()` 方法
- 新增 `hasDeliveryFiles()` 方法
- 增强 `approveDelivery()` 方法
- 增强 `rejectDelivery()` 方法
3. `yss-project/src/modules/project/pages/project-detail/stages/stage-delivery.component.scss`
- 美化审批按钮样式(绿色+红色)
- 添加 `.approval-hint` 样式
- 添加 `pulse` 动画
## ✅ 完成总结
现在从组长看板进入交付执行阶段时:
- ✅ 显示**真正的审批和驳回按钮**(不是测试按钮)
- ✅ 按钮精美,带悬停动画(上浮+放大)
- ✅ 通过审批功能完整,数据同步正确
- ✅ 驳回功能完整,必须输入原因
- ✅ 支持未提交状态直接审批
- ✅ 无文件时按钮禁用,显示提示
- ✅ 详细的控制台日志,方便调试
**测试URL**:
```
http://localhost:4200/wxwork/cDL6R1hgSi/project/iKvYck89zE/delivery?roleName=team-leader
```
您现在可以测试完整的审批流程了!🎉